forked from userexec/Sendy-Invoicing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinvoicing.php
444 lines (406 loc) · 15.2 KB
/
invoicing.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
<?php
// SENDY INVOICE PAYMENT INTERFACE
// SECURITY TOKEN MANAGEMENT
/* This script requires use of a security token system to allow an
* AJAX request to trigger the sending of an email containing invoice
* information to the payee. This script generates a token which is
* then stored in the database and hashed with the campaign name
* into a variable. When the invoice is printed, an AJAX request then
* sends the hash and invoice information to payment-notifier.php, which
* should have received the current campaign name, retrieves currently
* available security tokens, and hashes each to check for a match to the
* received hash. If a match is found, it removes the used token from the
* database and sends the invoice details to the payee. If no match is
* found, it will not attempt to send an email. payment-notifier.php will
* remove outdated tokens each time it runs to prevent excessive storage
* utilization.
*
* This security token system is necessary to prevent unauthorized or
* inappropriate usage of the PHP mailer functionality on the Sendy
* server. Without it an attacker may repeatedly call payment-notifier.php
* to abuse the system's email capabilities.
*
* For the system to function, the Sendy SQL database needs a table
* security_tokens with the following fields:
*
* id - INT
* date - DATETIME
* token - VARCHAR
*
* You can create this table by running the following query on your
* database, or simply allow this script to create the table for you.
*
* CREATE TABLE security_tokens (id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY
* KEY, date DATETIME NOT NULL, token VARCHAR(120) NOT NULL);
*
* The section of the code in which the table is detected or created
* can be commented out if you run the above query and do not wish this
* script to verify the table's existence each time it is run.
*/
?>
<?php include('../config.php');?>
<?php include('../functions.php');?>
<?php include('../login/auth.php');?>
<?php include('header-invoice.php');?>
<?php include('config.php');?>
<?php
//POST data
$schedule = $_POST['pay-and-schedule'];
$cron = $_POST['cron'];
if($schedule=='true') {
$campaign_id = is_numeric($_POST['campaign_id']) ? $_POST['campaign_id'] : exit;
$app = is_numeric($_POST['app']) ? $_POST['app'] : exit;
$email_list = $_POST['email_lists'];
$paypal_email = $_POST['paypal2'];
$total = $_POST['grand_total_val2'];
$send_date = $_POST['send_date'];
$hour = $_POST['hour'];
$ampm = $_POST['ampm'];
if($ampm=='pm' && $hour!=12)
$hour += 12;
if($ampm=='am' && $hour==12)
$hour = 00;
$min = $_POST['min'];
$timezone = $_POST['timezone'];
$send_date_array = explode('-', $send_date);
$month = $send_date_array[0];
$day = $send_date_array[1];
$year = $send_date_array[2];
$the_date = mktime($hour, $min, 0, $month, $day, $year);
$total_recipients = mysqli_real_escape_string($mysqli, $_POST['total_recipients2']);
} else {
$campaign_id = mysqli_real_escape_string($mysqli, $_POST['cid']);
$app = mysqli_real_escape_string($mysqli, $_POST['uid']);
$email_list = $_POST['email_list'];
$paypal_email = $_POST['paypal'];
$total = $_POST['grand_total_val'];
$send_date = date("m-d-Y");
$email_list_implode = implode(',', $email_list);
$total_recipients = mysqli_real_escape_string($mysqli, $_POST['total_recipients']);
}
// Set language
$q = "SELECT login.language FROM campaigns, login WHERE campaigns.id = '$campaign_id' AND login.app = campaigns.app";
$r = mysqli_query($mysqli, $q);
if ($r && mysqli_num_rows($r) > 0) while($row = mysqli_fetch_array($r)) $language = $row['language'];
set_locale($language);
// Get updated cron entry from main account
$q = "SELECT cron FROM login WHERE id = 1";
$r = mysqli_query($mysqli, $q);
if ($r) {
while($row = mysqli_fetch_array($r)) {
$cron = $row['cron'];
}
}
// Get currency
$q = "SELECT currency FROM apps WHERE id = '$app'";
$r = mysqli_query($mysqli, $q);
if ($r) {
while($row = mysqli_fetch_array($r)) {
$currency = $row['currency'];
}
}
// Get campaign name
$q = "SELECT title FROM campaigns WHERE id = '$campaign_id'";
$r = mysqli_query($mysqli, $q);
if ($r) {
while($row = mysqli_fetch_array($r)) {
$campaign = $row['title'];
}
}
// Get username
$q = 'SELECT name, company FROM login WHERE id = "'.get_app_info('userID').'"';
$r = mysqli_query($mysqli, $q);
if ($r) {
while($row = mysqli_fetch_array($r)) {
$name = $row['name'];
$department = $row['company'];
}
}
// Get fees
$q = 'SELECT delivery_fee, cost_per_recipient FROM apps WHERE app_name = "'.$department.'"';
$r = mysqli_query($mysqli, $q);
if ($r) {
while($row = mysqli_fetch_array($r)) {
$delivery_fee = $row['delivery_fee'];
$cost_per_recipient = $row['cost_per_recipient'];
}
$recipient_charges = $total_recipients * $cost_per_recipient;
}
/* SECURE TOKEN DATABASE TABLE CHECKER */
/* The MySQL database will need a security_tokens table with specific rows.
* This function checks for the existence of this table and creates it
* if necessary. If this table has already been created, feel free to
* comment this out.
*/
function checkSecurityTokenTable($mysqli) {
$q = 'SHOW TABLES LIKE "security_tokens"';
$r = mysqli_query($mysqli, $q);
if (mysqli_num_rows($r) === 0) {
$q = 'CREATE TABLE security_tokens (id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, date DATETIME NOT NULL, token VARCHAR(120) NOT NULL);';
$r = mysqli_query($mysqli, $q);
}
}
checkSecurityTokenTable($mysqli);
/* SECURE TOKEN GENERATION */
/* The following two functions rely upon openssl_random_pseudo_bytes() to
* choose an entropy source and generate a random number for use in the
* secure token exchange with payment-notifier.php. PHP 5.3+ required.
*/
function crypto_rand_secure($min, $max) {
$range = $max - $min;
if ($range < 0) return $min; // not so random...
$log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd >= $range);
return $min + $rnd;
}
function getToken($length=32){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
for($i=0;$i<$length;$i++){
$token .= $codeAlphabet[crypto_rand_secure(0,strlen($codeAlphabet))];
}
return $token;
}
/* SECURE TOKEN STORAGE */
/* The generated secure token will now be stored in the database for later
* retrieval, comparison, and removal by payment-notifier.php
*/
function newSecurityToken($mysqli) {
$token = getToken();
$q = 'INSERT INTO security_tokens (date, token) VALUES ("' . date("Y-m-d H:i:s") . '", "' . $token . '")';
$r = mysqli_query($mysqli, $q);
return $token;
}
// Store a token and generate its hash to be sent with the AJAX request
$hash = md5(newSecurityToken($mysqli) . $campaign);
?>
<style>
.container, .container-fluid {
position: relative;
}
.row {
padding: 1em;
}
#invoice {
margin: 3rem auto;
}
#itemization table {
width: 100%;
}
#accept_infobox {
padding: 1em;
border-radius: 8px;
}
#send_infobox {
padding: 1em;
border-radius: 8px;
}
#printAgain {
position: absolute;
top: 25%;
left: 0px;
width: 100%;
height: 30%;
text-align: center;
z-index: 1;
}
#printAgainContainer {
display: inline-block;
border-radius: 3rem;
background: lightgray;
padding: 2rem;
}
#printIcon {
display: inline-block;
background: gray;
color: white;
border-radius: 50%;
font-size: 14rem;
line-height: 20rem;
width: 20rem;
height: 20rem;
}
#invoice {
border: 1px solid #DDD;
box-shadow: 0px 10px 35px -15px black;
}
@media print {
#invoice {
opacity: 1 !important;
}
#buttons, #utility_header, #printAgain {
display: none;
opacity: 0;
}
}
</style>
<div id="invoiceContainer" class="container-fluid">
<div id="printAgain">
<div id="printAgainContainer">
<div id="printIcon">
<i class="fa fa-print"></i>
</div>
<br><br>
<a href="javascript:window.print()">
<button type="button" class="btn btn-primary">Print again</button>
</a>
</div>
</div>
<br><br>
<div id="invoice" class="container">
<div id="letterhead" class="row">
<div class="col-md-8">
<h1><?php echo $invoice_info['company_name']; ?></h1>
<p>
<?php echo $invoice_info['address_line_1']; ?>
<br>
<?php echo $invoice_info['address_line_2']; ?>
</p>
<p>
<?php echo $invoice_info['phone_number']; ?>
<br>
<a href="mailto:<?php echo $invoice_info['email_address']; ?>"><?php echo $invoice_info['email_address']; ?></a>
</p>
</div>
<div class="col-md-4" style="text-align: right;">
<h1 style="font-weight: bold;">INVOICE</h1>
<p>
<?php echo date('F j\, Y');?>
<br>
<?php echo $invoice_info['invoice_for']; ?>
</p>
<p>
<strong>Attn: <?php echo $name?></strong>
<br>
<?php echo $department?>
</p>
</div>
</div>
<hr>
<div id="message" class="row">
<p>Dear <?php echo $name?>,</p>
<br>
<p>Please find below the charges associated with the <strong><?php echo htmlentities($campaign, ENT_QUOTES);?></strong> mass email campaign, to be sent on <?php echo ($send_date ? htmlentities($send_date, ENT_QUOTES) : date("m-d-Y")); ?>. This email will be sent to <?php echo htmlentities($total_recipients, ENT_QUOTES);?> recipients.</span></p>
<p>Please make payment at your earliest convenience, and do not hesitate to contact us with any questions. You may JE the $<?php echo htmlentities($total, ENT_QUOTES);?> to the following account: <?php echo $invoice_info['account_number']; ?>.</p>
<br>
<p>Thanks,</p>
<p><?php echo $invoice_info['company_name_long']; ?></p>
</div>
<div id="itemization" class="row">
<table class="table table-striped table-bordered">
<tbody>
<tr>
<th class="active">#</th>
<th class="active">Item Description</th>
<th class="active">Quantity</th>
<th class="active">Unit Price</th>
<th class="active">Total</th>
</tr>
<tr>
<td>1</td>
<td>Amazon SES usage fee</td>
<td>1</td>
<td><?php echo htmlentities($delivery_fee, ENT_QUOTES);?></td>
<td><?php echo htmlentities($delivery_fee, ENT_QUOTES);?></td>
</tr>
<tr>
<td>2</td>
<td>Recipient fee</td>
<td><?php echo htmlentities($total_recipients, ENT_QUOTES);?></td>
<td><?php echo htmlentities($cost_per_recipient, ENT_QUOTES);?></td>
<td><?php echo htmlentities($recipient_charges, ENT_QUOTES);?></td>
</tr>
<tr>
<td class="success" colspan="4">
Total
</td>
<td class="success">
<?php echo htmlentities($total, ENT_QUOTES);?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="buttons" class="container">
<!-- The accept box is shown to users before they have accepted and printed the invoice. -->
<div id="accept_infobox" class="row bg-info">
<p>Before you can proceed and send this email, you must print this invoice. Please click the button below to accept the charges and print, and a "Send or schedule campaign" button will appear. A copy of the invoice will be automatically sent to <?php echo $invoice_info['company_name_long']; ?>.</p>
<!-- Accept charges -->
<a href="javascript:window.print()" onclick="acceptCharges()">
<button type="button" class="btn btn-primary">Accept charges and print invoice</button>
</a>
<!-- Cancel -->
<a href="<?php echo htmlentities(APP_PATH, ENT_QUOTES);?>/send-to?i=<?php echo htmlentities($app, ENT_QUOTES);?>&c=<?php echo htmlentities($campaign_id, ENT_QUOTES);?>">
<button type="button" class="btn btn-danger">Cancel</button>
</a>
</div>
<!-- The send box is shown to users after printing and contains a button to send the campaign. This is necessary in case the user needs to print the invoice again -->
<div id="send_infobox" class="row bg-success">
<p>Thank you! Your invoice has been recorded. <strong>Please ensure that you have printed a copy of your invoice</strong>, and then press the "Send or schedule campaign" button. You will receive an email notification when your campaign has been sent.</p>
<!-- Send campaign -->
<a href="
<?php if ($schedule == 'true') : ?>
<?php echo htmlentities(APP_PATH, ENT_QUOTES);?>/sending?i=<?php echo htmlentities($app, ENT_QUOTES);?>&c=<?php echo htmlentities($campaign_id, ENT_QUOTES);?>&e=<?php echo htmlentities($email_list, ENT_QUOTES);?>&s=true&cr=<?php echo htmlentities($cron, ENT_QUOTES);?>&date=<?php echo htmlentities($the_date, ENT_QUOTES);?>&timezone=<?php echo htmlentities($timezone, ENT_QUOTES);?>&recipients=<?php echo htmlentities($total_recipients, ENT_QUOTES);?>
<?php else : ?>
<?php echo htmlentities(APP_PATH, ENT_QUOTES);?>/sending?i=<?php echo htmlentities($app, ENT_QUOTES)?>&c=<?php echo htmlentities($campaign_id, ENT_QUOTES);?>&e=<?php echo htmlentities($email_list_implode, ENT_QUOTES);?>&s=false&cr=<?php echo htmlentities($cron, ENT_QUOTES);?>&recipients=<?php echo htmlentities($total_recipients, ENT_QUOTES);?>
<?php endif; ?>
">
<button type="button" class="btn btn-success">Send or schedule campaign</button>
</a>
</div>
</div>
<script>
$('#send_infobox').hide();
$('#printAgain').hide();
function acceptCharges() {
notifyPayee();
revealSend();
}
function revealSend() {
$('#accept_infobox').hide();
$('#send_infobox').show();
$('#invoice').css('opacity', '0.3');
$('#printAgain').show();
}
<?php
/* PAYMENT NOTIFIER */
/* The following AJAX request posts data to payment-notifier.php
* when the user accepts charges and prints the invoice.
* payment-notifier.php will then verify the security token
* to ensure it's clear to send an email and check a hash of the
* monetary values it received against the "verify" hash to ensure
* the user did not tamper with the request.
*/
?>
function notifyPayee() {
$.ajax({
url: 'payment-notifier.php',
type: 'POST',
cache: false,
data: 'api_key=<?php echo $email_notification["api_key"]; ?>' +
'&token=<?php echo $hash; ?>' +
'&verify=<?php echo md5($hash . $delivery_fee . $cost_per_recipient . $total); ?>' +
'&campaign=<?php echo $campaign; ?>' +
'&name=<?php echo urlencode($name); ?>' +
'&department=<?php echo urlencode($department); ?>' +
'&send_date=<?php echo $send_date; ?>' +
'&total_recipients=<?php echo $total_recipients; ?>' +
'&delivery_fee=<?php echo $delivery_fee; ?>' +
'&cost_per_recipient=<?php echo $cost_per_recipient; ?>' +
'&recipient_charges=<?php echo $recipient_charges; ?>' +
'&total=<?php echo $total; ?>'
});
}
</script>
</body>
</html>