parses paypal soap logs
1#include <cstdio> 2#include <iostream> 3#include <fstream> 4#include <string> 5#include <vector> 6#include <map> 7#include <regex> 8#include <algorithm> 9#include <numeric> 10#include <iomanip> 11#include <unordered_map> 12#include <getopt.h> 13 14// Transaction data structure 15struct Transaction { 16 int transNum; 17 std::string amount; 18 std::string currency; 19 std::string firstName; 20 std::string lastName; 21 std::string street; 22 std::string city; 23 std::string state; 24 std::string zip; 25 std::string ccType; 26 std::string ccLast4; 27 std::string expMonth; 28 std::string expYear; 29 std::string cvv; 30 std::string transId; 31 std::string status; 32 std::string corrId; 33 std::string procAmount; 34}; 35 36// Response data structure 37struct Response { 38 std::string transId; 39 std::string status; 40 std::string corrId; 41 std::string procAmount; 42}; 43 44// Function prototypes 45void showHelp(const char* programName); 46std::string extractXmlValue(const std::string& xml, const std::string& tag); 47std::string extractXmlAttribute(const std::string& xml, const std::string& attribute); 48std::vector<std::string> extractRequests(const std::string& logContent); 49std::vector<std::string> extractResponses(const std::string& logContent); 50std::vector<Response> parseResponses(const std::vector<std::string>& responseXmls); 51std::vector<Transaction> parseTransactions(const std::vector<std::string>& requestXmls, const std::vector<Response>& responses); 52void outputRawData(const std::vector<Transaction>& transactions); 53void outputSummary(const std::vector<Transaction>& transactions); 54 55int main(int argc, char* argv[]) { 56 // Default options 57 bool summaryOnly = false; 58 std::string logFile; 59 60 // Parse command line options 61 static struct option longOptions[] = { 62 {"help", no_argument, 0, 'h'}, 63 {"summary", no_argument, 0, 's'}, 64 {"raw", no_argument, 0, 'r'}, 65 {0, 0, 0, 0} 66 }; 67 68 int optionIndex = 0; 69 int opt; 70 while ((opt = getopt_long(argc, argv, "hsr", longOptions, &optionIndex)) != -1) { 71 switch (opt) { 72 case 'h': 73 showHelp(argv[0]); 74 return 0; 75 case 's': 76 summaryOnly = true; 77 break; 78 case 'r': 79 summaryOnly = false; 80 break; 81 case '?': 82 std::cerr << "Unknown option: " << static_cast<char>(optopt) << std::endl; 83 showHelp(argv[0]); 84 return 1; 85 default: 86 break; 87 } 88 } 89 90 // Get logfile name 91 if (optind < argc) { 92 logFile = argv[optind]; 93 } else { 94 std::cerr << "Error: No logfile specified" << std::endl; 95 showHelp(argv[0]); 96 return 1; 97 } 98 99 // Check if file exists 100 std::ifstream file(logFile); 101 if (!file.is_open()) { 102 std::cerr << "Error: File '" << logFile << "' not found" << std::endl; 103 return 1; 104 } 105 106 // Read the entire file 107 std::string logContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); 108 file.close(); 109 110 // Extract requests and responses 111 std::vector<std::string> requestXmls = extractRequests(logContent); 112 std::vector<std::string> responseXmls = extractResponses(logContent); 113 114 // Parse responses 115 std::vector<Response> responses = parseResponses(responseXmls); 116 117 // Parse transactions 118 std::vector<Transaction> transactions = parseTransactions(requestXmls, responses); 119 120 // Output data 121 if (summaryOnly) { 122 outputSummary(transactions); 123 } else { 124 outputRawData(transactions); 125 } 126 127 return 0; 128} 129 130void showHelp(const char* programName) { 131 std::cout << "PayPal SOAP Log Parser\n\n"; 132 std::cout << "USAGE:\n"; 133 std::cout << " " << programName << " [OPTIONS] <logfile>\n\n"; 134 std::cout << "OPTIONS:\n"; 135 std::cout << " -h, --help Show this help message\n"; 136 std::cout << " -s, --summary Show summary statistics only\n"; 137 std::cout << " -r, --raw Output raw structured data (default)\n\n"; 138 std::cout << "OUTPUT FORMAT (tab-separated):\n"; 139 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 std::cout << "FIELD DESCRIPTIONS:\n"; 141 std::cout << " TRANS_NUM - Transaction sequence number\n"; 142 std::cout << " AMOUNT - Order total amount\n"; 143 std::cout << " CURRENCY - Currency code (USD, etc)\n"; 144 std::cout << " FIRSTNAME - Customer first name\n"; 145 std::cout << " LASTNAME - Customer last name\n"; 146 std::cout << " STREET - Street address\n"; 147 std::cout << " CITY - City name\n"; 148 std::cout << " STATE - State/Province code\n"; 149 std::cout << " ZIP - Postal code\n"; 150 std::cout << " CCTYPE - Credit card type (Visa, MasterCard, etc)\n"; 151 std::cout << " CCLAST4 - Last 4 digits of credit card\n"; 152 std::cout << " EXPMONTH - Card expiration month\n"; 153 std::cout << " EXPYEAR - Card expiration year\n"; 154 std::cout << " CVV - CVV code\n"; 155 std::cout << " TRANSID - PayPal transaction ID\n"; 156 std::cout << " STATUS - Transaction status (Success/Failure)\n"; 157 std::cout << " CORRID - Correlation ID\n"; 158 std::cout << " PROC_AMOUNT - Actually processed amount\n\n"; 159 std::cout << "EXAMPLES:\n"; 160 std::cout << " # Get all transactions\n"; 161 std::cout << " " << programName << " payments.log\n\n"; 162 std::cout << " # Get only successful transactions\n"; 163 std::cout << " " << programName << " payments.log | grep Success\n\n"; 164 std::cout << " # Count transactions by state\n"; 165 std::cout << " " << programName << " payments.log | cut -d'|' -f8 | sort | uniq -c | sort -nr\n\n"; 166 std::cout << " # Find largest transaction\n"; 167 std::cout << " " << programName << " payments.log | sort -t'|' -k2 -nr | head -1\n\n"; 168 std::cout << " # Get transactions over $500\n"; 169 std::cout << " " << programName << " payments.log | awk -F'|' '$2 > 500'\n\n"; 170 std::cout << " # Summary stats\n"; 171 std::cout << " " << programName << " -s payments.log\n"; 172} 173 174std::string extractXmlValue(const std::string& xml, const std::string& tag) { 175 std::regex pattern("<" + tag + "(?:[^>]*)>([^<]*)</" + tag + ">"); 176 std::smatch match; 177 if (std::regex_search(xml, match, pattern) && match.size() > 1) { 178 return match[1].str(); 179 } 180 return ""; 181} 182 183std::string extractXmlAttribute(const std::string& xml, const std::string& attribute) { 184 std::regex pattern(attribute + "=\"([^\"]*)\""); 185 std::smatch match; 186 if (std::regex_search(xml, match, pattern) && match.size() > 1) { 187 return match[1].str(); 188 } 189 return ""; 190} 191 192std::vector<std::string> extractRequests(const std::string& logContent) { 193 std::vector<std::string> requests; 194 std::regex pattern("PPAPIService: Request: (.*)"); 195 196 std::string::const_iterator searchStart(logContent.cbegin()); 197 std::smatch match; 198 while (std::regex_search(searchStart, logContent.cend(), match, pattern)) { 199 if (match.size() > 1) { 200 requests.push_back(match[1].str()); 201 } 202 searchStart = match.suffix().first; 203 } 204 205 return requests; 206} 207 208std::vector<std::string> extractResponses(const std::string& logContent) { 209 std::vector<std::string> responses; 210 std::regex pattern("PPAPIService: Response: <\\?.*\\?>(.*)"); 211 212 std::string::const_iterator searchStart(logContent.cbegin()); 213 std::smatch match; 214 while (std::regex_search(searchStart, logContent.cend(), match, pattern)) { 215 if (match.size() > 1) { 216 responses.push_back(match[1].str()); 217 } 218 searchStart = match.suffix().first; 219 } 220 221 return responses; 222} 223 224std::vector<Response> parseResponses(const std::vector<std::string>& responseXmls) { 225 std::vector<Response> responses; 226 227 for (const auto& xml : responseXmls) { 228 Response response; 229 response.transId = extractXmlValue(xml, "TransactionID"); 230 response.status = extractXmlValue(xml, "Ack"); 231 response.corrId = extractXmlValue(xml, "CorrelationID"); 232 response.procAmount = extractXmlValue(xml, "Amount"); 233 234 responses.push_back(response); 235 } 236 237 return responses; 238} 239 240std::vector<Transaction> parseTransactions(const std::vector<std::string>& requestXmls, const std::vector<Response>& responses) { 241 std::vector<Transaction> transactions; 242 int transNum = 1; 243 244 for (size_t i = 0; i < requestXmls.size(); ++i) { 245 const auto& xml = requestXmls[i]; 246 247 Transaction transaction; 248 transaction.transNum = transNum++; 249 250 // Extract request fields 251 transaction.amount = extractXmlValue(xml, "ebl:OrderTotal"); 252 transaction.currency = extractXmlAttribute(xml, "currencyID"); 253 transaction.firstName = extractXmlValue(xml, "ebl:FirstName"); 254 transaction.lastName = extractXmlValue(xml, "ebl:LastName"); 255 transaction.street = extractXmlValue(xml, "ebl:Street1"); 256 transaction.city = extractXmlValue(xml, "ebl:CityName"); 257 transaction.state = extractXmlValue(xml, "ebl:StateOrProvince"); 258 transaction.zip = extractXmlValue(xml, "ebl:PostalCode"); 259 transaction.ccType = extractXmlValue(xml, "ebl:CreditCardType"); 260 transaction.ccLast4 = extractXmlValue(xml, "ebl:CreditCardLastFourDigits"); 261 transaction.expMonth = extractXmlValue(xml, "ebl:ExpMonth"); 262 transaction.expYear = extractXmlValue(xml, "ebl:ExpYear"); 263 transaction.cvv = extractXmlValue(xml, "ebl:CVV2"); 264 265 // Get corresponding response data 266 if (i < responses.size()) { 267 transaction.transId = responses[i].transId; 268 transaction.status = responses[i].status; 269 transaction.corrId = responses[i].corrId; 270 transaction.procAmount = responses[i].procAmount; 271 } 272 273 transactions.push_back(transaction); 274 } 275 276 return transactions; 277} 278 279void outputRawData(const std::vector<Transaction>& transactions) { 280 for (const auto& t : transactions) { 281 std::cout << t.transNum << "|" 282 << t.amount << "|" 283 << t.currency << "|" 284 << t.firstName << "|" 285 << t.lastName << "|" 286 << t.street << "|" 287 << t.city << "|" 288 << t.state << "|" 289 << t.zip << "|" 290 << t.ccType << "|" 291 << t.ccLast4 << "|" 292 << t.expMonth << "|" 293 << t.expYear << "|" 294 << t.cvv << "|" 295 << t.transId << "|" 296 << t.status << "|" 297 << t.corrId << "|" 298 << t.procAmount << std::endl; 299 } 300} 301 302void outputSummary(const std::vector<Transaction>& transactions) { 303 std::cout << "=== SUMMARY ===" << std::endl; 304 305 // Count transactions 306 int total = transactions.size(); 307 int successful = std::count_if(transactions.begin(), transactions.end(), 308 [](const Transaction& t) { return t.status == "Success"; }); 309 310 std::cout << "Total Transactions: " << total << std::endl; 311 std::cout << "Successful: " << successful << std::endl; 312 std::cout << "Failed: " << (total - successful) << std::endl; 313 std::cout << std::endl; 314 315 // Top 5 states 316 std::map<std::string, int> stateCounts; 317 for (const auto& t : transactions) { 318 stateCounts[t.state]++; 319 } 320 321 std::cout << "Top 5 States by Transaction Count:" << std::endl; 322 std::vector<std::pair<std::string, int>> stateCountVec(stateCounts.begin(), stateCounts.end()); 323 std::sort(stateCountVec.begin(), stateCountVec.end(), 324 [](const auto& a, const auto& b) { return a.second > b.second; }); 325 326 int count = 0; 327 for (const auto& sc : stateCountVec) { 328 if (count++ >= 5) break; 329 std::cout << " " << sc.first << ": " << sc.second << std::endl; 330 } 331 std::cout << std::endl; 332 333 // Transaction amount stats 334 std::vector<double> amounts; 335 for (const auto& t : transactions) { 336 try { 337 amounts.push_back(std::stod(t.amount)); 338 } catch (...) { 339 // Skip invalid amounts 340 } 341 } 342 343 if (!amounts.empty()) { 344 double totalAmount = std::accumulate(amounts.begin(), amounts.end(), 0.0); 345 double largest = *std::max_element(amounts.begin(), amounts.end()); 346 double smallest = *std::min_element(amounts.begin(), amounts.end()); 347 348 std::cout << "Transaction Amount Stats:" << std::endl; 349 std::cout << " Total: $" << std::fixed << std::setprecision(2) << totalAmount << std::endl; 350 std::cout << " Largest: $" << std::fixed << std::setprecision(2) << largest << std::endl; 351 std::cout << " Smallest: $" << std::fixed << std::setprecision(2) << smallest << std::endl; 352 } 353}