parses paypal soap logs

chore: add .gitignore file

- Ignore build artifacts and test data
- Ignore nix environment files
- Ignore editor and OS-specific files

💙 Generated with Crush
Co-Authored-By: 💙 Crush <crush@charm.land>

dunkirk.sh 8342599f a4e67237

verified
Changed files
+46 -26
src
+21
.gitignore
···
+
# Build artifacts
+
/build/
+
/result
+
/transaction-parser
+
+
# Nix
+
.direnv/
+
.envrc
+
+
# Test data
+
*.log
+
+
# Editor files
+
.vscode/
+
.idea/
+
*.swp
+
*~
+
+
# OS files
+
.DS_Store
+
Thumbs.db
+25 -26
src/transaction-parser.cpp
···
#include <algorithm>
#include <numeric>
#include <iomanip>
-
#include <unordered_map>
#include <getopt.h>
// Transaction data structure
···
std::cout << " -h, --help Show this help message\n";
std::cout << " -s, --summary Show summary statistics only\n";
std::cout << " -r, --raw Output raw structured data (default)\n\n";
-
std::cout << "OUTPUT FORMAT (tab-separated):\n";
+
std::cout << "OUTPUT FORMAT:\n";
std::cout << " TRANS_NUM|AMOUNT|CURRENCY|FIRSTNAME|LASTNAME|STREET|CITY|STATE|ZIP|CCTYPE|CCLAST4|EXPMONTH|EXPYEAR|CVV|TRANSID|STATUS|CORRID|PROC_AMOUNT\n\n";
std::cout << "FIELD DESCRIPTIONS:\n";
std::cout << " TRANS_NUM - Transaction sequence number\n";
···
std::vector<std::string> extractRequests(const std::string& logContent) {
std::vector<std::string> requests;
std::regex pattern("PPAPIService: Request: (.*)");
-
+
std::string::const_iterator searchStart(logContent.cbegin());
std::smatch match;
while (std::regex_search(searchStart, logContent.cend(), match, pattern)) {
···
}
searchStart = match.suffix().first;
}
-
+
return requests;
}
std::vector<std::string> extractResponses(const std::string& logContent) {
std::vector<std::string> responses;
std::regex pattern("PPAPIService: Response: <\\?.*\\?>(.*)");
-
+
std::string::const_iterator searchStart(logContent.cbegin());
std::smatch match;
while (std::regex_search(searchStart, logContent.cend(), match, pattern)) {
···
}
searchStart = match.suffix().first;
}
-
+
return responses;
}
std::vector<Response> parseResponses(const std::vector<std::string>& responseXmls) {
std::vector<Response> responses;
-
+
for (const auto& xml : responseXmls) {
Response response;
response.transId = extractXmlValue(xml, "TransactionID");
response.status = extractXmlValue(xml, "Ack");
response.corrId = extractXmlValue(xml, "CorrelationID");
response.procAmount = extractXmlValue(xml, "Amount");
-
+
responses.push_back(response);
}
-
+
return responses;
}
std::vector<Transaction> parseTransactions(const std::vector<std::string>& requestXmls, const std::vector<Response>& responses) {
std::vector<Transaction> transactions;
int transNum = 1;
-
+
for (size_t i = 0; i < requestXmls.size(); ++i) {
const auto& xml = requestXmls[i];
-
+
Transaction transaction;
transaction.transNum = transNum++;
-
+
// Extract request fields
transaction.amount = extractXmlValue(xml, "ebl:OrderTotal");
transaction.currency = extractXmlAttribute(xml, "currencyID");
···
transaction.expMonth = extractXmlValue(xml, "ebl:ExpMonth");
transaction.expYear = extractXmlValue(xml, "ebl:ExpYear");
transaction.cvv = extractXmlValue(xml, "ebl:CVV2");
-
+
// Get corresponding response data
if (i < responses.size()) {
transaction.transId = responses[i].transId;
···
transaction.corrId = responses[i].corrId;
transaction.procAmount = responses[i].procAmount;
}
-
+
transactions.push_back(transaction);
}
-
+
return transactions;
}
···
void outputSummary(const std::vector<Transaction>& transactions) {
std::cout << "=== SUMMARY ===" << std::endl;
-
+
// Count transactions
int total = transactions.size();
-
int successful = std::count_if(transactions.begin(), transactions.end(),
+
int successful = std::count_if(transactions.begin(), transactions.end(),
[](const Transaction& t) { return t.status == "Success"; });
-
+
std::cout << "Total Transactions: " << total << std::endl;
std::cout << "Successful: " << successful << std::endl;
std::cout << "Failed: " << (total - successful) << std::endl;
std::cout << std::endl;
-
+
// Top 5 states
std::map<std::string, int> stateCounts;
for (const auto& t : transactions) {
stateCounts[t.state]++;
}
-
+
std::cout << "Top 5 States by Transaction Count:" << std::endl;
std::vector<std::pair<std::string, int>> stateCountVec(stateCounts.begin(), stateCounts.end());
-
std::sort(stateCountVec.begin(), stateCountVec.end(),
+
std::sort(stateCountVec.begin(), stateCountVec.end(),
[](const auto& a, const auto& b) { return a.second > b.second; });
-
+
int count = 0;
for (const auto& sc : stateCountVec) {
if (count++ >= 5) break;
std::cout << " " << sc.first << ": " << sc.second << std::endl;
}
std::cout << std::endl;
-
+
// Transaction amount stats
std::vector<double> amounts;
for (const auto& t : transactions) {
···
// Skip invalid amounts
}
}
-
+
if (!amounts.empty()) {
double totalAmount = std::accumulate(amounts.begin(), amounts.end(), 0.0);
double largest = *std::max_element(amounts.begin(), amounts.end());
double smallest = *std::min_element(amounts.begin(), amounts.end());
-
+
std::cout << "Transaction Amount Stats:" << std::endl;
std::cout << " Total: $" << std::fixed << std::setprecision(2) << totalAmount << std::endl;
std::cout << " Largest: $" << std::fixed << std::setprecision(2) << largest << std::endl;
std::cout << " Smallest: $" << std::fixed << std::setprecision(2) << smallest << std::endl;
}
-
}
+
}