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}