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
+46 -26
+21
.gitignore
··· 1 + # Build artifacts 2 + /build/ 3 + /result 4 + /transaction-parser 5 + 6 + # Nix 7 + .direnv/ 8 + .envrc 9 + 10 + # Test data 11 + *.log 12 + 13 + # Editor files 14 + .vscode/ 15 + .idea/ 16 + *.swp 17 + *~ 18 + 19 + # OS files 20 + .DS_Store 21 + Thumbs.db
+25 -26
src/transaction-parser.cpp
··· 8 8 #include <algorithm> 9 9 #include <numeric> 10 10 #include <iomanip> 11 - #include <unordered_map> 12 11 #include <getopt.h> 13 12 14 13 // Transaction data structure ··· 135 134 std::cout << " -h, --help Show this help message\n"; 136 135 std::cout << " -s, --summary Show summary statistics only\n"; 137 136 std::cout << " -r, --raw Output raw structured data (default)\n\n"; 138 - std::cout << "OUTPUT FORMAT (tab-separated):\n"; 137 + std::cout << "OUTPUT FORMAT:\n"; 139 138 std::cout << " TRANS_NUM|AMOUNT|CURRENCY|FIRSTNAME|LASTNAME|STREET|CITY|STATE|ZIP|CCTYPE|CCLAST4|EXPMONTH|EXPYEAR|CVV|TRANSID|STATUS|CORRID|PROC_AMOUNT\n\n"; 140 139 std::cout << "FIELD DESCRIPTIONS:\n"; 141 140 std::cout << " TRANS_NUM - Transaction sequence number\n"; ··· 192 191 std::vector<std::string> extractRequests(const std::string& logContent) { 193 192 std::vector<std::string> requests; 194 193 std::regex pattern("PPAPIService: Request: (.*)"); 195 - 194 + 196 195 std::string::const_iterator searchStart(logContent.cbegin()); 197 196 std::smatch match; 198 197 while (std::regex_search(searchStart, logContent.cend(), match, pattern)) { ··· 201 200 } 202 201 searchStart = match.suffix().first; 203 202 } 204 - 203 + 205 204 return requests; 206 205 } 207 206 208 207 std::vector<std::string> extractResponses(const std::string& logContent) { 209 208 std::vector<std::string> responses; 210 209 std::regex pattern("PPAPIService: Response: <\\?.*\\?>(.*)"); 211 - 210 + 212 211 std::string::const_iterator searchStart(logContent.cbegin()); 213 212 std::smatch match; 214 213 while (std::regex_search(searchStart, logContent.cend(), match, pattern)) { ··· 217 216 } 218 217 searchStart = match.suffix().first; 219 218 } 220 - 219 + 221 220 return responses; 222 221 } 223 222 224 223 std::vector<Response> parseResponses(const std::vector<std::string>& responseXmls) { 225 224 std::vector<Response> responses; 226 - 225 + 227 226 for (const auto& xml : responseXmls) { 228 227 Response response; 229 228 response.transId = extractXmlValue(xml, "TransactionID"); 230 229 response.status = extractXmlValue(xml, "Ack"); 231 230 response.corrId = extractXmlValue(xml, "CorrelationID"); 232 231 response.procAmount = extractXmlValue(xml, "Amount"); 233 - 232 + 234 233 responses.push_back(response); 235 234 } 236 - 235 + 237 236 return responses; 238 237 } 239 238 240 239 std::vector<Transaction> parseTransactions(const std::vector<std::string>& requestXmls, const std::vector<Response>& responses) { 241 240 std::vector<Transaction> transactions; 242 241 int transNum = 1; 243 - 242 + 244 243 for (size_t i = 0; i < requestXmls.size(); ++i) { 245 244 const auto& xml = requestXmls[i]; 246 - 245 + 247 246 Transaction transaction; 248 247 transaction.transNum = transNum++; 249 - 248 + 250 249 // Extract request fields 251 250 transaction.amount = extractXmlValue(xml, "ebl:OrderTotal"); 252 251 transaction.currency = extractXmlAttribute(xml, "currencyID"); ··· 261 260 transaction.expMonth = extractXmlValue(xml, "ebl:ExpMonth"); 262 261 transaction.expYear = extractXmlValue(xml, "ebl:ExpYear"); 263 262 transaction.cvv = extractXmlValue(xml, "ebl:CVV2"); 264 - 263 + 265 264 // Get corresponding response data 266 265 if (i < responses.size()) { 267 266 transaction.transId = responses[i].transId; ··· 269 268 transaction.corrId = responses[i].corrId; 270 269 transaction.procAmount = responses[i].procAmount; 271 270 } 272 - 271 + 273 272 transactions.push_back(transaction); 274 273 } 275 - 274 + 276 275 return transactions; 277 276 } 278 277 ··· 301 300 302 301 void outputSummary(const std::vector<Transaction>& transactions) { 303 302 std::cout << "=== SUMMARY ===" << std::endl; 304 - 303 + 305 304 // Count transactions 306 305 int total = transactions.size(); 307 - int successful = std::count_if(transactions.begin(), transactions.end(), 306 + int successful = std::count_if(transactions.begin(), transactions.end(), 308 307 [](const Transaction& t) { return t.status == "Success"; }); 309 - 308 + 310 309 std::cout << "Total Transactions: " << total << std::endl; 311 310 std::cout << "Successful: " << successful << std::endl; 312 311 std::cout << "Failed: " << (total - successful) << std::endl; 313 312 std::cout << std::endl; 314 - 313 + 315 314 // Top 5 states 316 315 std::map<std::string, int> stateCounts; 317 316 for (const auto& t : transactions) { 318 317 stateCounts[t.state]++; 319 318 } 320 - 319 + 321 320 std::cout << "Top 5 States by Transaction Count:" << std::endl; 322 321 std::vector<std::pair<std::string, int>> stateCountVec(stateCounts.begin(), stateCounts.end()); 323 - std::sort(stateCountVec.begin(), stateCountVec.end(), 322 + std::sort(stateCountVec.begin(), stateCountVec.end(), 324 323 [](const auto& a, const auto& b) { return a.second > b.second; }); 325 - 324 + 326 325 int count = 0; 327 326 for (const auto& sc : stateCountVec) { 328 327 if (count++ >= 5) break; 329 328 std::cout << " " << sc.first << ": " << sc.second << std::endl; 330 329 } 331 330 std::cout << std::endl; 332 - 331 + 333 332 // Transaction amount stats 334 333 std::vector<double> amounts; 335 334 for (const auto& t : transactions) { ··· 339 338 // Skip invalid amounts 340 339 } 341 340 } 342 - 341 + 343 342 if (!amounts.empty()) { 344 343 double totalAmount = std::accumulate(amounts.begin(), amounts.end(), 0.0); 345 344 double largest = *std::max_element(amounts.begin(), amounts.end()); 346 345 double smallest = *std::min_element(amounts.begin(), amounts.end()); 347 - 346 + 348 347 std::cout << "Transaction Amount Stats:" << std::endl; 349 348 std::cout << " Total: $" << std::fixed << std::setprecision(2) << totalAmount << std::endl; 350 349 std::cout << " Largest: $" << std::fixed << std::setprecision(2) << largest << std::endl; 351 350 std::cout << " Smallest: $" << std::fixed << std::setprecision(2) << smallest << std::endl; 352 351 } 353 - } 352 + }