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