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.
This document describes common integration patterns and best practices for integrating with the Chargeworx payment platform.
API integration patterns
OAuth2 client credentials flow
The recommended authentication method for API access:
// 1. Request access token
var client = new HttpClient ();
var request = new HttpRequestMessage ( HttpMethod . Post ,
"https://api.chargeworx.com/connect/token" );
request . Content = new FormUrlEncodedContent ( new []
{
new KeyValuePair < string , string >( "grant_type" , "client_credentials" ),
new KeyValuePair < string , string >( "client_id" , "your_client_id" ),
new KeyValuePair < string , string >( "client_secret" , "your_client_secret" ),
new KeyValuePair < string , string >( "scope" , "PaymentScope" )
});
var response = await client . SendAsync ( request );
var token = await response . Content . ReadAsAsync < TokenResponse >();
// 2. Use token for API calls
client . DefaultRequestHeaders . Authorization =
new AuthenticationHeaderValue ( "Bearer" , token . AccessToken );
var transactionResponse = await client . PostAsync (
"/api/pay/{projectId}/transactions" ,
transactionContent );
Best practices :
Cache tokens until expiration
Implement token refresh logic
Use HTTPS for all requests
Store credentials securely
API key authentication
Alternative authentication for simpler integrations:
var client = new HttpClient ();
client . DefaultRequestHeaders . Add ( "X-API-Key" , "your_api_key" );
var response = await client . PostAsync (
"/api/pay/{projectId}/transactions" ,
transactionContent );
Best practices :
Rotate API keys regularly
Use different keys per environment
Monitor key usage
Revoke compromised keys immediately
Transaction processing patterns
Simple authorization
Basic credit card authorization:
var request = new
{
merchantReference = "ORDER-12345" ,
amount = 100.00 ,
currency = "USD" ,
cardNumber = "4111111111111111" ,
expirationMonth = "12" ,
expirationYear = "2025" ,
cvv = "123" ,
billTo = new
{
firstName = "John" ,
lastName = "Doe" ,
street1 = "123 Main St" ,
city = "San Francisco" ,
state = "CA" ,
postalCode = "94105" ,
country = "US" ,
email = "john@example.com"
}
};
var response = await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/transactions" ,
request );
var result = await response . Content . ReadAsAsync < TransactionResponse >();
Authorization with capture
Authorize and immediately capture:
var request = new
{
merchantReference = "ORDER-12345" ,
amount = 100.00 ,
currency = "USD" ,
cardNumber = "4111111111111111" ,
expirationMonth = "12" ,
expirationYear = "2025" ,
cvv = "123" ,
captureImmediately = true , // Capture right away
billTo = new { /* ... */ }
};
Delayed capture
Authorize now, capture later:
// 1. Authorize
var authRequest = new
{
merchantReference = "ORDER-12345" ,
amount = 100.00 ,
currency = "USD" ,
cardNumber = "4111111111111111" ,
expirationMonth = "12" ,
expirationYear = "2025" ,
cvv = "123" ,
captureImmediately = false // Don't capture yet
};
var authResponse = await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/transactions" ,
authRequest );
var authResult = await authResponse . Content . ReadAsAsync < TransactionResponse >();
// 2. Capture later (within 7 days)
var captureResponse = await client . PostAsync (
$"/api/adm/transactions/{ authResult . TransactionId }/capture" ,
null );
Tokenized payments
Use saved payment info for recurring charges:
// 1. Save payment info
var saveRequest = new
{
merchantReference = "CUSTOMER-001" ,
cardNumber = "4111111111111111" ,
expirationMonth = "12" ,
expirationYear = "2025" ,
billTo = new { /* ... */ }
};
var saveResponse = await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/companyProjectCreditCardPaymentsInfo" ,
saveRequest );
var paymentInfo = await saveResponse . Content
. ReadAsAsync < PaymentInfoResponse >();
// 2. Charge using token
var chargeRequest = new
{
merchantReference = "ORDER-12346" ,
amount = 50.00 ,
currency = "USD" ,
paymentInfoId = paymentInfo . Id // Use saved payment info
};
var chargeResponse = await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/transactions" ,
chargeRequest );
Webhook integration
Receiving transaction notifications
Set up webhook endpoint to receive transaction updates:
[ HttpPost ]
[ Route ( "webhooks/chargeworx/transactions" )]
public async Task < IActionResult > ReceiveTransactionWebhook (
[ FromBody ] TransactionWebhook webhook )
{
// Verify webhook signature
if ( ! VerifySignature ( webhook ))
return Unauthorized ();
// Process webhook
switch ( webhook . EventType )
{
case "transaction.approved" :
await HandleApprovedTransaction ( webhook . Transaction );
break ;
case "transaction.declined" :
await HandleDeclinedTransaction ( webhook . Transaction );
break ;
case "chargeback.received" :
await HandleChargeback ( webhook . Chargeback );
break ;
}
return Ok ();
}
private bool VerifySignature ( TransactionWebhook webhook )
{
var signature = Request . Headers [ "X-Chargeworx-Signature" ];
var payload = JsonConvert . SerializeObject ( webhook );
var expectedSignature = ComputeHmacSha256 ( payload , _webhookSecret );
return signature == expectedSignature ;
}
Best practices :
Verify webhook signatures
Process webhooks asynchronously
Return 200 OK quickly
Implement idempotency
Log all webhook events
Batch processing patterns
Bulk transaction import
Import multiple transactions at once:
// 1. Prepare CSV file
var csv = new StringBuilder ();
csv . AppendLine ( "MerchantReference,Amount,Currency,CardNumber,ExpirationMonth,ExpirationYear" );
csv . AppendLine ( "ORDER-001,100.00,USD,4111111111111111,12,2025" );
csv . AppendLine ( "ORDER-002,50.00,USD,5555555555554444,06,2026" );
// 2. Upload file
var content = new MultipartFormDataContent ();
content . Add ( new StringContent ( csv . ToString ()), "file" , "transactions.csv" );
var response = await client . PostAsync (
$"/api/adm/import" ,
content );
var importResult = await response . Content . ReadAsAsync < ImportResponse >();
// 3. Poll for completion
while ( importResult . Status == "Processing" )
{
await Task . Delay ( 5000 );
var statusResponse = await client . GetAsync (
$"/api/adm/import/{ importResult . BatchId }" );
importResult = await statusResponse . Content . ReadAsAsync < ImportResponse >();
}
Account updater integration
Keep card information current:
// 1. Enable account updater for project
var config = new
{
accountUpdaterEnabled = true ,
updateFrequency = "Monthly"
};
await client . PutAsJsonAsync (
$"/api/adm/companyProjects/{ projectId }/settings" ,
config );
// 2. Mark cards for update
var paymentInfoIds = new [] { guid1 , guid2 , guid3 };
await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/companyProjectCreditCardPaymentsInfo/setAccountUpdaterPriority" ,
new { paymentInfoIds , priority = 1 });
// 3. Check update results
var results = await client . GetAsync (
$"/api/pay/{ projectId }/companyProjectCreditCardPaymentsInfo/getCreditCardUpdaterResult" );
var updaterResults = await results . Content
. ReadAsAsync < List < AccountUpdaterResult >>();
Error handling patterns
Retry logic
Implement exponential backoff for transient errors:
public async Task < TransactionResponse > ProcessTransactionWithRetry (
TransactionRequest request )
{
var maxRetries = 3 ;
var delay = TimeSpan . FromSeconds ( 1 );
for ( int i = 0 ; i < maxRetries ; i ++ )
{
try
{
var response = await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/transactions" ,
request );
if ( response . IsSuccessStatusCode )
{
return await response . Content
. ReadAsAsync < TransactionResponse >();
}
// Don't retry client errors (4xx)
if (( int ) response . StatusCode >= 400 &&
( int ) response . StatusCode < 500 )
{
throw new InvalidOperationException (
$"Client error: { response . StatusCode }" );
}
}
catch ( HttpRequestException ex )
{
if ( i == maxRetries - 1 )
throw ;
await Task . Delay ( delay );
delay = TimeSpan . FromSeconds ( delay . TotalSeconds * 2 );
}
}
throw new Exception ( "Max retries exceeded" );
}
Idempotency
Use idempotency keys to prevent duplicate transactions:
var idempotencyKey = Guid . NewGuid (). ToString ();
var request = new
{
merchantReference = "ORDER-12345" ,
amount = 100.00 ,
currency = "USD" ,
cardNumber = "4111111111111111" ,
expirationMonth = "12" ,
expirationYear = "2025"
};
client . DefaultRequestHeaders . Add ( "X-Idempotency-Key" , idempotencyKey );
var response = await client . PostAsJsonAsync (
$"/api/pay/{ projectId }/transactions" ,
request );
// If request fails and is retried, same idempotency key
// will return the original transaction
Reporting integration
Transaction reports
Query transactions with filters:
var query = new
{
startDate = DateTime . UtcNow . AddDays ( - 30 ),
endDate = DateTime . UtcNow ,
status = "Approved" ,
minAmount = 10.00 ,
maxAmount = 1000.00
};
var response = await client . PostAsJsonAsync (
$"/api/adm/reportTransactions/search" ,
query );
var transactions = await response . Content
. ReadAsAsync < List < ReportTransaction >>();
Export to CSV
Download transaction data:
var query = new
{
startDate = DateTime . UtcNow . AddDays ( - 30 ),
endDate = DateTime . UtcNow ,
format = "csv"
};
var response = await client . PostAsJsonAsync (
$"/api/adm/reportTransactions/export" ,
query );
var csvContent = await response . Content . ReadAsStringAsync ();
File . WriteAllText ( "transactions.csv" , csvContent );
Multi-tenancy patterns
Company and project isolation
Each API call is scoped to a specific project:
// Different projects have different configurations
var project1Id = Guid . Parse ( "..." );
var project2Id = Guid . Parse ( "..." );
// Transaction for project 1
await client . PostAsJsonAsync (
$"/api/pay/{ project1Id }/transactions" ,
transaction1 );
// Transaction for project 2 (different processor, settings)
await client . PostAsJsonAsync (
$"/api/pay/{ project2Id }/transactions" ,
transaction2 );
User access control
Users can belong to multiple companies and projects:
// Get user's companies
var companies = await client . GetAsync ( "/api/adm/companies" );
// Get projects for a company
var projects = await client . GetAsync (
$"/api/adm/companyProjects?companyId={ companyId }" );
// Switch context to different company
await client . PostAsJsonAsync (
"/api/adm/users/switchCompany" ,
new { companyId });
Testing patterns
Test mode
Use test credentials for development:
var testConfig = new
{
apiBaseUrl = "https://test-api.chargeworx.com" ,
clientId = "test_client_id" ,
clientSecret = "test_client_secret"
};
// Test card numbers
var testCards = new []
{
"4111111111111111" , // Visa - Approved
"5555555555554444" , // Mastercard - Approved
"4000000000000002" , // Visa - Declined
};
Mocked responses
Configure mocked processor responses:
var request = new
{
merchantReference = "TEST-001" ,
amount = 100.00 ,
currency = "USD" ,
cardNumber = "4111111111111111" ,
expirationMonth = "12" ,
expirationYear = "2025" ,
mockResponse = "Approved" // Force specific response
};
Connection pooling
Reuse HTTP connections:
// Use HttpClientFactory
services . AddHttpClient < IChargeworxClient , ChargeworxClient >( client =>
{
client . BaseAddress = new Uri ( "https://api.chargeworx.com" );
client . Timeout = TimeSpan . FromSeconds ( 30 );
});
// Client is injected and reused
public class PaymentService
{
private readonly IChargeworxClient _client ;
public PaymentService ( IChargeworxClient client )
{
_client = client ;
}
}
Response caching
Cache frequently accessed data:
// Cache processor configuration
var cacheKey = $"processor:{ projectId }" ;
var processor = await _cache . GetOrCreateAsync ( cacheKey , async entry =>
{
entry . AbsoluteExpirationRelativeToNow = TimeSpan . FromMinutes ( 30 );
var response = await client . GetAsync (
$"/api/adm/projectPaymentProcessors?projectId={ projectId }" );
return await response . Content
. ReadAsAsync < ProcessorConfiguration >();
});
Batch operations
Process multiple items in a single request:
// Instead of multiple single requests
foreach ( var transaction in transactions )
{
await ProcessTransactionAsync ( transaction ); // Slow
}
// Use batch endpoint
await client . PostAsJsonAsync (
$"/api/adm/transactions/batch" ,
transactions ); // Fast
Security best practices
Secure credential storage
Never hardcode credentials:
// Bad
var clientSecret = "hardcoded_secret" ;
// Good - use configuration
var clientSecret = _configuration [ "Chargeworx:ClientSecret" ];
// Better - use secrets manager
var clientSecret = await _secretsManager . GetSecretAsync (
"chargeworx-client-secret" );
PCI compliance
Handle card data securely:
// Never log full card numbers
_logger . LogInformation ( "Processing card ending in {Last4}" ,
cardNumber . Substring ( cardNumber . Length - 4 ));
// Use tokenization
var paymentInfo = await SavePaymentInfoAsync ( cardNumber );
// Store only token, not card number
await StoreTokenAsync ( paymentInfo . Id );
// Encrypt sensitive data
var encrypted = _encryptionService . Encrypt ( cardNumber );
IP whitelisting
Restrict API access by IP:
// Configure allowed IPs in admin UI
var whitelist = new []
{
"192.168.1.100" ,
"10.0.0.0/8"
};
await client . PostAsJsonAsync (
$"/api/adm/whitelistIP" ,
new { ipAddresses = whitelist });
Next steps
API reference Complete API documentation
Authentication OAuth2 and API key setup
Data flow Payment processing flow
Component interactions How services communicate