mirror of https://github.com/itflow-org/itflow
Merge pull request #1148 from itflow-org/stripe-autopay
Initial add Stripe Auto-payment with saved card
This commit is contained in:
commit
5567dfe968
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$stripe_clients_sql = mysqli_query($mysqli, "SELECT * FROM client_stripe LEFT JOIN clients ON client_stripe.client_id = clients.client_id");
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-credit-card mr-2"></i>Online Payment - Client info</h3>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Client</th>
|
||||
<th>Stripe Customer ID</th>
|
||||
<th>Stripe Payment ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($stripe_clients_sql)) {
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = sanitizeInput($row['client_name']);
|
||||
$stripe_id = sanitizeInput($row['stripe_id']);
|
||||
$stripe_pm = sanitizeInput($row['stripe_pm']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><?php echo "$client_name ($client_id)" ?></td>
|
||||
<td><?php echo $stripe_id; ?></td>
|
||||
<td><?php echo $stripe_pm ?></td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
|
||||
|
|
@ -2404,10 +2404,16 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
|
|||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.7.5'");
|
||||
}
|
||||
|
||||
// if (CURRENT_DATABASE_VERSION == '1.7.5') {
|
||||
// // Insert queries here required to update to DB version 1.7.6
|
||||
if (CURRENT_DATABASE_VERSION == '1.7.5') {
|
||||
mysqli_query($mysqli, "CREATE TABLE `client_stripe` (`client_id` INT(11) NOT NULL, `stripe_id` VARCHAR(255) NOT NULL, `stripe_pm` varchar(255) NULL) ENGINE = InnoDB CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci; ");
|
||||
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.7.6'");
|
||||
}
|
||||
|
||||
// if (CURRENT_DATABASE_VERSION == '1.7.6') {
|
||||
// // Insert queries here required to update to DB version 1.7.7
|
||||
// // Then, update the database to the next sequential version
|
||||
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.7.6'");
|
||||
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.7.7'");
|
||||
// }
|
||||
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
* It is used in conjunction with database_updates.php
|
||||
*/
|
||||
|
||||
DEFINE("LATEST_DATABASE_VERSION", "1.7.5");
|
||||
DEFINE("LATEST_DATABASE_VERSION", "1.7.6");
|
||||
|
|
|
|||
11
db.sql
11
db.sql
|
|
@ -342,6 +342,17 @@ CREATE TABLE `client_notes` (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `client_stripe`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `client_stripe`;
|
||||
CREATE TABLE IF NOT EXISTS `client_stripe` (
|
||||
`client_id` int(11) NOT NULL,
|
||||
`stripe_id` varchar(255) NOT NULL,
|
||||
`stripe_pm` varchar(255) NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
--
|
||||
-- Table structure for table `client_tags`
|
||||
--
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent
|
|||
|
||||
// Add Payment to History
|
||||
mysqli_query($mysqli, "INSERT INTO payments SET payment_date = '$pi_date', payment_amount = $pi_amount_paid, payment_currency_code = '$pi_currency', payment_account_id = $config_stripe_account, payment_method = 'Stripe', payment_reference = 'Stripe - $pi_id', payment_invoice_id = $invoice_id");
|
||||
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Paid', history_description = 'Payment added - $ip - $os - $browser', history_invoice_id = $invoice_id");
|
||||
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Paid', history_description = 'Online Payment added (client) - $ip - $os - $browser', history_invoice_id = $invoice_id");
|
||||
|
||||
// Notify
|
||||
appNotify("Invoice Paid", "Invoice $invoice_prefix$invoice_number has been paid by $client_name - $ip - $os - $browser", "invoice.php?invoice_id=$invoice_id", $pi_client_id);
|
||||
|
|
@ -313,7 +313,7 @@ if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent
|
|||
|
||||
if (!empty($config_smtp_host)) {
|
||||
$subject = "Payment Received - Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello $contact_name,<br><br>We have received your payment in the amount of " . $pi_currency . $pi_amount_paid . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount: " . numfmt_format_currency($currency_format, $pi_amount_paid, $invoice_currency_code) . "<br>Balance: " . numfmt_format_currency($currency_format, '0', $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>~<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
$body = "Hello $contact_name,<br><br>We have received online payment for the amount of " . $pi_currency . $pi_amount_paid . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount: " . numfmt_format_currency($currency_format, $pi_amount_paid, $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>~<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$data = [
|
||||
[
|
||||
|
|
@ -330,7 +330,7 @@ if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent
|
|||
// Email the internal notification address too
|
||||
if (!empty($config_invoice_paid_notification_email)) {
|
||||
$subject = "Payment Received - $client_name - Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello, <br><br>This is a notification that an invoice has been paid in ITFlow. Below is a copy of the receipt sent to the client:-<br><br>--------<br><br>Hello $contact_name,<br><br>We have received your payment in the amount of " . $pi_currency . $pi_amount_paid . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount: " . numfmt_format_currency($currency_format, $pi_amount_paid, $invoice_currency_code) . "<br>Balance: " . numfmt_format_currency($currency_format, '0', $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>~<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
$body = "Hello, <br><br>This is a notification that an invoice has been paid in ITFlow. Below is a copy of the receipt sent to the client:-<br><br>--------<br><br>Hello $contact_name,<br><br>We have received online payment for the amount of " . $pi_currency . $pi_amount_paid . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount: " . numfmt_format_currency($currency_format, $pi_amount_paid, $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>~<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$data[] = [
|
||||
'from' => $config_invoice_from_email,
|
||||
|
|
|
|||
16
invoice.php
16
invoice.php
|
|
@ -145,6 +145,17 @@ if (isset($_GET['invoice_id'])) {
|
|||
$json_products = json_encode($products);
|
||||
}
|
||||
|
||||
// Payment with saved card (auto-pay)
|
||||
if ($config_stripe_enable) {
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE client_id = $client_id LIMIT 1"));
|
||||
if ($stripe_client_details) {
|
||||
$stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
$stripe_pm = sanitizeInput($stripe_client_details['stripe_pm']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
|
|
@ -197,6 +208,11 @@ if (isset($_GET['invoice_id'])) {
|
|||
<a class="btn btn-success" href="#" data-toggle="modal" data-target="#addPaymentModal">
|
||||
<i class="fa fa-fw fa-credit-card mr-2"></i>Add Payment
|
||||
</a>
|
||||
<?php if ($invoice_status !== 'Partial' && $config_stripe_enable && $stripe_id && $stripe_pm) { ?>
|
||||
<a class="btn btn-primary confirm-link" href="post.php?add_payment_stripe&invoice_id=<?php echo $invoice_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token']; ?>">
|
||||
<i class="fa fa-fw fa-credit-card mr-2"></i>Pay via saved card
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (($invoice_status == 'Sent' || $invoice_status == 'Viewed') && $invoice_amount == 0 && $invoice_status !== 'Non-Billable') { ?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
// Initialize Stripe.js
|
||||
const stripe = Stripe('pk_test_51OTpmkHRGkC845Mqz0zM2A1pjnnXwOyD5tyPzWnRwVthuizNjuBIjoYgMHBMLQBuegrUXQpIyX4yr1fNMo7QzCs500bBnFJgEr');
|
||||
|
||||
initialize();
|
||||
|
||||
// Fetch Checkout Session and retrieve the client secret
|
||||
async function initialize() {
|
||||
const fetchClientSecret = async () => {
|
||||
const response = await fetch("/portal/portal_post.php?create_stripe_checkout", {
|
||||
method: "POST",
|
||||
});
|
||||
const { clientSecret } = await response.json();
|
||||
return clientSecret;
|
||||
};
|
||||
|
||||
// Initialize Checkout
|
||||
const checkout = await stripe.initEmbeddedCheckout({
|
||||
fetchClientSecret,
|
||||
});
|
||||
|
||||
// Mount Checkout
|
||||
checkout.mount('#checkout');
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Auto-pay configuration for PTC/finance contacts
|
||||
*/
|
||||
|
||||
require_once "inc_portal.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_publishable = nullable_htmlentities($stripe_vars['config_stripe_publishable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
// Get client's StripeID from database
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
|
||||
if ($stripe_client_details) {
|
||||
$stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
$stripe_pm = sanitizeInput($stripe_client_details['stripe_pm']);
|
||||
}
|
||||
|
||||
// Stripe not enabled in settings
|
||||
if (!$config_stripe_enable || !$config_stripe_publishable || !$config_stripe_secret) {
|
||||
echo "Stripe payment error - Stripe is not enabled, please talk to your helpdesk for further information.";
|
||||
include_once 'portal_footer.php';
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>AutoPay</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<!-- Setup pt1: Stripe ID not found / auto-payment not configured -->
|
||||
<?php if (!$stripe_client_details || empty($stripe_id)) { ?>
|
||||
|
||||
<b>Save card details</b><br>
|
||||
In order to set up automatic payments, you must create a customer record in Stripe.<br>
|
||||
First, you must authorize Stripe to store your card details for the purpose of automatic payment.
|
||||
<br><br>
|
||||
|
||||
<div class="col-5">
|
||||
<form action="portal_post.php" method="POST">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="consent" name="consent" value="1" required>
|
||||
<label for="consent" class="custom-control-label">
|
||||
I grant consent for automatic payments
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="form-control btn-success" name="create_stripe_customer">Create Stripe Customer Record</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php }
|
||||
|
||||
// Setup pt2: Stripe ID found / payment may be configured -->
|
||||
elseif (empty($stripe_pm)) { ?>
|
||||
|
||||
<b>Save card details</b><br>
|
||||
Please add the payment details you would like to save.<br>
|
||||
By adding payment details here, you grant consent for future automatic payments of invoices.<br><br>
|
||||
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<script src="../js/autopay_setup_stripe.js"></script>
|
||||
<input type="hidden" id="stripe_publishable_key" value="<?php echo $config_stripe_publishable ?>">
|
||||
<div id="checkout">
|
||||
<!-- Checkout will insert the payment form here -->
|
||||
</div>
|
||||
|
||||
<?php }
|
||||
|
||||
// Manage the saved card
|
||||
else { ?>
|
||||
|
||||
<b>Manage saved card details</b>
|
||||
|
||||
<?php
|
||||
|
||||
try {
|
||||
// Initialize
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Get payment method info (last 4 digits etc)
|
||||
$payment_method = $stripe->customers->retrievePaymentMethod(
|
||||
$stripe_id,
|
||||
$stripe_pm,
|
||||
[]
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when fetching payment method info for $stripe_pm: $error");
|
||||
logApp("Stripe", "error", "Exception when fetching payment method info for $stripe_pm: $error");
|
||||
}
|
||||
|
||||
$card_name = nullable_htmlentities($payment_method->billing_details->name);
|
||||
$card_brand = nullable_htmlentities($payment_method->card->display_brand);
|
||||
$card_last4 = nullable_htmlentities($payment_method->card->last4);
|
||||
$card_expires = nullable_htmlentities($payment_method->card->exp_month) . "/" . nullable_htmlentities($payment_method->card->exp_year);
|
||||
|
||||
?>
|
||||
|
||||
<ul><li><?php echo "$card_name - $card_brand card ending in $card_last4, expires $card_expires"; ?></li></ul>
|
||||
|
||||
<hr>
|
||||
<b>Actions</b><br>
|
||||
- <a href="portal_post.php?stripe_remove_card&pm=<?php echo $stripe_pm; ?>">Remove saved card</a>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
require_once "portal_footer.php";
|
||||
|
|
@ -8,7 +8,6 @@ header("Content-Security-Policy: default-src 'self'");
|
|||
|
||||
require_once "inc_portal.php";
|
||||
|
||||
|
||||
?>
|
||||
<div class="col-md-2 offset-1">
|
||||
<a href="ticket_add.php" class="btn btn-primary btn-block">New ticket</a>
|
||||
|
|
|
|||
|
|
@ -50,28 +50,34 @@ header("X-Frame-Options: DENY"); // Legacy
|
|||
</li>
|
||||
|
||||
<?php if (($session_contact_primary == 1 || $session_contact_is_billing_contact) && $config_module_enable_accounting == 1) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "invoices.php") {echo "active";} ?>" href="invoices.php">Invoices</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "quotes.php") {echo "active";} ?>" href="quotes.php">Quotes</a>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle <?php echo in_array(basename($_SERVER['PHP_SELF']), ['invoices.php', 'quotes.php', 'autopay.php']) ? 'active' : ''; ?>" href="#" id="navbarDropdown1" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Finance
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown1">
|
||||
<a class="dropdown-item" href="invoices.php">Invoices</a>
|
||||
<a class="dropdown-item" href="quotes.php">Quotes</a>
|
||||
<a class="dropdown-item" href="autopay.php">Auto Payment</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($config_module_enable_itdoc && ($session_contact_primary == 1 || $session_contact_is_technical_contact)) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "documents.php") {echo "active";} ?>" href="documents.php">Documents</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "contacts.php") {echo "active";} ?>" href="contacts.php">Contacts</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "domains.php") {echo "active";} ?>" href="domains.php">Domains</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "certificates.php") {echo "active";} ?>" href="certificates.php">Certificates</a>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle <?php echo in_array(basename($_SERVER['PHP_SELF']), ['documents.php', 'contacts.php', 'domains.php', 'certificates.php']) ? 'active' : ''; ?>" href="#" id="navbarDropdown2" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Technical
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown2">
|
||||
<a class="dropdown-item" href="contacts.php">Contacts</a>
|
||||
<a class="dropdown-item" href="documents.php">Documents</a>
|
||||
<a class="dropdown-item" href="domains.php">Domains</a>
|
||||
<a class="dropdown-item" href="certificates.php">Certificates</a>
|
||||
<a class="dropdown-item" href="ticket_view_all.php">All tickets</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
|
||||
</ul><!-- End left nav -->
|
||||
|
||||
<ul class="nav navbar-nav pull-right">
|
||||
<li class="nav-item dropdown">
|
||||
|
|
@ -100,7 +106,6 @@ header("X-Frame-Options: DENY"); // Legacy
|
|||
<img src="<?php echo "../uploads/clients/$session_client_id/$session_contact_photo"; ?>" alt="..." height="50" width="50" class="img-circle img-responsive">
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<span class="fa-stack fa-2x rounded-left">
|
||||
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
|
||||
<span class="fa fa-stack-1x text-white"><?php echo $session_contact_initials; ?></span>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
* Process GET/POST requests
|
||||
*/
|
||||
|
||||
require_once "inc_portal.php";
|
||||
|
||||
require_once '../config.php';
|
||||
require_once '../get_settings.php';
|
||||
require_once '../functions.php';
|
||||
require_once 'check_login.php';
|
||||
require_once 'portal_functions.php';
|
||||
|
||||
if (isset($_POST['add_ticket'])) {
|
||||
|
||||
|
|
@ -327,6 +330,12 @@ if (isset($_POST['edit_profile'])) {
|
|||
}
|
||||
|
||||
if (isset($_POST['add_contact'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$contact_name = sanitizeInput($_POST['contact_name']);
|
||||
$contact_email = sanitizeInput($_POST['contact_email']);
|
||||
$contact_technical = intval($_POST['contact_technical']);
|
||||
|
|
@ -368,6 +377,12 @@ if (isset($_POST['add_contact'])) {
|
|||
}
|
||||
|
||||
if (isset($_POST['edit_contact'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$contact_id = intval($_POST['contact_id']);
|
||||
$contact_name = sanitizeInput($_POST['contact_name']);
|
||||
$contact_email = sanitizeInput($_POST['contact_email']);
|
||||
|
|
@ -413,3 +428,258 @@ if (isset($_POST['edit_contact'])) {
|
|||
|
||||
customAction('contact_update', $contact_id);
|
||||
}
|
||||
|
||||
if (isset($_POST['create_stripe_customer'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Include stripe SDK
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
|
||||
// Get client's StripeID from database (should be none)
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT stripe_id FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
|
||||
if (!$stripe_client_details) {
|
||||
|
||||
try {
|
||||
// Initiate Stripe
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Create customer
|
||||
$customer = $stripe->customers->create([
|
||||
'name' => $session_client_name,
|
||||
'email' => $session_contact_email,
|
||||
'metadata' => [
|
||||
'itflow_client_id' => $session_client_id,
|
||||
'consent' => $session_contact_name
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when creating customer record for $session_client_name: $error");
|
||||
logApp("Stripe", "error", "Exception creating customer $session_client_name: $error");
|
||||
}
|
||||
|
||||
// Get & Store customer ID
|
||||
$stripe_id = sanitizeInput($customer->id);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO client_stripe SET client_id = $session_client_id, stripe_id = '$stripe_id'");
|
||||
|
||||
// Logging
|
||||
logAction("Stripe", "Create", "$session_contact_name created Stripe customer for $session_client_name as $stripe_id and authorised future automatic payments", $session_client_id, $session_client_id);
|
||||
|
||||
$_SESSION['alert_message'] = "Stripe customer created, thank you for your consent";
|
||||
|
||||
} else {
|
||||
$_SESSION['alert_type'] = "danger";
|
||||
$_SESSION['alert_message'] = "Stripe customer already exists";
|
||||
}
|
||||
|
||||
header('Location: autopay.php');
|
||||
}
|
||||
|
||||
if (isset($_GET['create_stripe_checkout'])) {
|
||||
|
||||
// This page is called by the autopay_setup_stripe.js, it returns a checkout session client secret
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Client Currency
|
||||
$client_currency_details = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT client_currency_code FROM clients WHERE client_id = $session_client_id LIMIT 1"));
|
||||
$client_currency = $client_currency_details['client_currency_code'];
|
||||
|
||||
// Define return URL that user is redirected to once payment method is verified by Stripe
|
||||
$return_url = "https://$config_base_url/portal/portal_post.php?stripe_save_card&session_id={CHECKOUT_SESSION_ID}";
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Create checkout session (server side)
|
||||
$checkout_session = $stripe->checkout->sessions->create([
|
||||
'currency' => $client_currency,
|
||||
'mode' => 'setup',
|
||||
'ui_mode' => 'embedded',
|
||||
'return_url' => $return_url,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when creating checkout session: $error");
|
||||
logApp("Stripe", "error", "Exception creating checkout: $error");
|
||||
}
|
||||
|
||||
// Return the client secret to the js script
|
||||
echo json_encode(array('clientSecret' => $checkout_session->client_secret));
|
||||
|
||||
// No redirect & no point logging this
|
||||
}
|
||||
|
||||
if (isset($_GET['stripe_save_card'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get session ID from URL
|
||||
$checkout_session_id = sanitizeInput($_GET['session_id']);
|
||||
|
||||
// Get client's StripeID from database
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT stripe_id FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
|
||||
$client_stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Retrieve checkout session
|
||||
$checkout_session = $stripe->checkout->sessions->retrieve($checkout_session_id,[]);
|
||||
|
||||
// Get setup intent
|
||||
$setup_intent_id = $checkout_session->setup_intent;
|
||||
|
||||
// Retrieve the setup intent details
|
||||
$setup_intent = $stripe->setupIntents->retrieve($setup_intent_id, []);
|
||||
|
||||
// Get the payment method token
|
||||
$payment_method = sanitizeInput($setup_intent->payment_method);
|
||||
|
||||
// Attach the payment method to the client in Stripe
|
||||
$stripe->paymentMethods->attach($payment_method, ['customer' => $client_stripe_id]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when adding payment method info: $error");
|
||||
logApp("Stripe", "error", "Exception adding payment method: $error");
|
||||
}
|
||||
|
||||
// Update ITFlow
|
||||
mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = '$payment_method' WHERE client_id = $session_client_id LIMIT 1");
|
||||
|
||||
// Get some card/payment method details for the email/logging
|
||||
$payment_method_details = $stripe->paymentMethods->retrieve($payment_method);
|
||||
$card_info = sanitizeInput($payment_method_details->card->display_brand) . " " . sanitizeInput($payment_method_details->card->last4);
|
||||
|
||||
// Send email confirmation
|
||||
|
||||
// Company Details & Settings
|
||||
$sql_settings = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
|
||||
$row = mysqli_fetch_array($sql_settings);
|
||||
$company_name = sanitizeInput($row['company_name']);
|
||||
$config_smtp_host = $row['config_smtp_host'];
|
||||
$config_smtp_port = intval($row['config_smtp_port']);
|
||||
$config_smtp_encryption = $row['config_smtp_encryption'];
|
||||
$config_smtp_username = $row['config_smtp_username'];
|
||||
$config_smtp_password = $row['config_smtp_password'];
|
||||
$config_invoice_from_name = sanitizeInput($row['config_invoice_from_name']);
|
||||
$config_invoice_from_email = sanitizeInput($row['config_invoice_from_email']);
|
||||
$config_base_url = sanitizeInput($config_base_url);
|
||||
|
||||
if (!empty($config_smtp_host)) {
|
||||
$subject = "Payment method saved";
|
||||
$body = "Hello $session_contact_name,<br><br>We’re writing to confirm that your payment details have been securely stored with Stripe, our trusted payment processor.<br><br>By agreeing to save your payment information, you have authorized us to automatically bill your card ($card_info) for any future invoices. The payment details you’ve provided are securely stored with Stripe and will be used solely for invoices. We do not have access to your full card details.<br><br>You may update or remove your payment information at any time using the portal.<br><br>Thank you for your business!<br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_invoice_from_email,
|
||||
'from_name' => $config_invoice_from_name,
|
||||
'recipient' => $session_contact_email,
|
||||
'recipient_name' => $session_contact_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
]
|
||||
];
|
||||
|
||||
$mail = addToMailQueue($mysqli, $data);
|
||||
|
||||
}
|
||||
|
||||
// Logging
|
||||
logAction("Stripe", "Update", "$session_contact_name added saved card ($card_info) for future automatic payments (PM: $payment_method)", $session_client_id, $session_client_id);
|
||||
|
||||
// Redirect
|
||||
$_SESSION['alert_message'] = "Card saved - thank you";
|
||||
header('Location: autopay.php');
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['stripe_remove_card'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: portal_post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$payment_method = sanitizeInput($_GET['pm']);
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Detach PM
|
||||
$stripe->paymentMethods->detach($payment_method, []);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error");
|
||||
logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error");
|
||||
}
|
||||
|
||||
// Remove payment method from ITFlow
|
||||
mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = NULL WHERE client_id = $session_client_id LIMIT 1");
|
||||
|
||||
// Logging & Redirect
|
||||
logAction("Stripe", "Update", "$session_contact_name deleted saved card (PM: $payment_method)", $session_client_id, $session_client_id);
|
||||
|
||||
$_SESSION['alert_message'] = "Card removed";
|
||||
header('Location: autopay.php');
|
||||
}
|
||||
|
|
@ -815,14 +815,17 @@ if (isset($_POST['add_payment'])) {
|
|||
|
||||
$email_data[] = $email;
|
||||
|
||||
// Add email to queue
|
||||
if (!empty($email)) {
|
||||
addToMailQueue($mysqli, $email_data);
|
||||
}
|
||||
|
||||
// Get Email ID for reference
|
||||
$email_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Email Logging
|
||||
|
||||
$_SESSION['alert_message'] = "Email queued successfully! <a class='text-bold text-light' href='admin_mail_queue.php'>Check Admin > Mail queue</a>";
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Email Receipt Queued', history_invoice_id = $invoice_id");
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id");
|
||||
logAction("Invoice", "Payment", "Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id", $client_id, $invoice_id);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -847,26 +850,22 @@ if (isset($_POST['add_payment'])) {
|
|||
|
||||
$email_data[] = $email;
|
||||
|
||||
// Add email to queue
|
||||
if (!empty($email)) {
|
||||
addToMailQueue($mysqli, $email_data);
|
||||
}
|
||||
|
||||
// Get Email ID for reference
|
||||
$email_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Email Logging
|
||||
|
||||
$_SESSION['alert_message'] = "Test email queued successfully! <a class='text-bold text-light' href='admin_mail_queue.php'>Check Admin > Mail queue</a>";
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id");
|
||||
|
||||
logAction("Invoice", "Payment", "Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id", $client_id, $invoice_id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add emails to queue
|
||||
if (!empty($email)) {
|
||||
addToMailQueue($mysqli, $email_data);
|
||||
}
|
||||
|
||||
//Update Invoice Status
|
||||
mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id");
|
||||
|
||||
|
|
@ -884,6 +883,193 @@ if (isset($_POST['add_payment'])) {
|
|||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['add_payment_stripe'])) {
|
||||
|
||||
enforceUserPermission('module_sales', 2);
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$invoice_id = intval($_GET['invoice_id']);
|
||||
|
||||
// Get invoice details
|
||||
$sql = mysqli_query($mysqli,"SELECT * FROM invoices
|
||||
LEFT JOIN clients ON invoice_client_id = client_id
|
||||
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
|
||||
WHERE invoice_id = $invoice_id"
|
||||
);
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$invoice_number = intval($row['invoice_number']);
|
||||
$invoice_status = sanitizeInput($row['invoice_status']);
|
||||
$invoice_amount = floatval($row['invoice_amount']);
|
||||
$invoice_prefix = sanitizeInput($row['invoice_prefix']);
|
||||
$invoice_number = intval($row['invoice_number']);
|
||||
$invoice_url_key = sanitizeInput($row['invoice_url_key']);
|
||||
$invoice_currency_code = sanitizeInput($row['invoice_currency_code']);
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = sanitizeInput($row['client_name']);
|
||||
$contact_name = sanitizeInput($row['contact_name']);
|
||||
$contact_email = sanitizeInput($row['contact_email']);
|
||||
$contact_phone = sanitizeInput(formatPhoneNumber($row['contact_phone']));
|
||||
$contact_extension = preg_replace("/[^0-9]/", '',$row['contact_extension']);
|
||||
$contact_mobile = sanitizeInput(formatPhoneNumber($row['contact_mobile']));
|
||||
|
||||
// Get ITFlow company details
|
||||
$sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$company_name = sanitizeInput($row['company_name']);
|
||||
$company_country = sanitizeInput($row['company_country']);
|
||||
$company_address = sanitizeInput($row['company_address']);
|
||||
$company_city = sanitizeInput($row['company_city']);
|
||||
$company_state = sanitizeInput($row['company_state']);
|
||||
$company_zip = sanitizeInput($row['company_zip']);
|
||||
$company_phone = sanitizeInput(formatPhoneNumber($row['company_phone']));
|
||||
$company_email = sanitizeInput($row['company_email']);
|
||||
$company_website = sanitizeInput($row['company_website']);
|
||||
|
||||
// Sanitize Config vars from get_settings.php
|
||||
$config_invoice_from_name = sanitizeInput($config_invoice_from_name);
|
||||
$config_invoice_from_email = sanitizeInput($config_invoice_from_email);
|
||||
|
||||
// Get Client Stripe details
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE client_id = $client_id LIMIT 1"));
|
||||
$stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
$stripe_pm = sanitizeInput($stripe_client_details['stripe_pm']);
|
||||
|
||||
// Sanity checks
|
||||
if (!$config_stripe_enable || !$stripe_id || !$stripe_pm) {
|
||||
$_SESSION['alert_type'] = "error";
|
||||
$_SESSION['alert_message'] = "Stripe not enabled or no client card saved";
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
exit();
|
||||
} elseif ($invoice_status !== 'Sent' && $invoice_status !== 'Viewed') {
|
||||
$_SESSION['alert_type'] = "error";
|
||||
$_SESSION['alert_message'] = "Invalid invoice state (draft/partial/paid/not billable)";
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
exit();
|
||||
} elseif ($invoice_amount == 0) {
|
||||
$_SESSION['alert_type'] = "error";
|
||||
$_SESSION['alert_message'] = "Invalid invoice amount";
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialize Stripe
|
||||
require_once __DIR__ . '/../../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
$balance_to_pay = round($invoice_amount, 2);
|
||||
$pi_description = "ITFlow: $client_name payment of $invoice_currency_code $balance_to_pay for $invoice_prefix$invoice_number";
|
||||
|
||||
// Create a payment intent
|
||||
try {
|
||||
$payment_intent = $stripe->paymentIntents->create([
|
||||
'amount' => intval($balance_to_pay * 100), // Times by 100 as Stripe expects values in cents
|
||||
'currency' => $invoice_currency_code,
|
||||
'customer' => $stripe_id,
|
||||
'payment_method' => $stripe_pm,
|
||||
'off_session' => true,
|
||||
'confirm' => true,
|
||||
'description' => $pi_description,
|
||||
'metadata' => [
|
||||
'itflow_client_id' => $client_id,
|
||||
'itflow_client_name' => $client_name,
|
||||
'itflow_invoice_number' => $invoice_prefix . $invoice_number,
|
||||
'itflow_invoice_id' => $invoice_id,
|
||||
]
|
||||
]);
|
||||
|
||||
// Get details from PI
|
||||
$pi_id = sanitizeInput($payment_intent->id);
|
||||
$pi_date = date('Y-m-d', $payment_intent->created);
|
||||
$pi_amount_paid = floatval(($payment_intent->amount_received / 100));
|
||||
$pi_currency = strtoupper(sanitizeInput($payment_intent->currency));
|
||||
$pi_livemode = $payment_intent->livemode;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception during payment intent for invoice ID $invoice_id / $invoice_prefix$invoice_number: $error");
|
||||
logApp("Stripe", "error", "Exception during PI for invoice ID $invoice_id: $error");
|
||||
}
|
||||
|
||||
if ($payment_intent->status == "succeeded" && intval($balance_to_pay) == intval($pi_amount_paid)) {
|
||||
|
||||
// Update Invoice Status
|
||||
mysqli_query($mysqli, "UPDATE invoices SET invoice_status = 'Paid' WHERE invoice_id = $invoice_id");
|
||||
|
||||
// Add Payment to History
|
||||
mysqli_query($mysqli, "INSERT INTO payments SET payment_date = '$pi_date', payment_amount = $pi_amount_paid, payment_currency_code = '$pi_currency', payment_account_id = $config_stripe_account, payment_method = 'Stripe', payment_reference = 'Stripe - $pi_id', payment_invoice_id = $invoice_id");
|
||||
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Paid', history_description = 'Online Payment added (agent)', history_invoice_id = $invoice_id");
|
||||
|
||||
// Email receipt
|
||||
if (!empty($config_smtp_host)) {
|
||||
$subject = "Payment Received - Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello $contact_name,<br><br>We have received online payment for the amount of " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount Paid: " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
// Queue Mail
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_invoice_from_email,
|
||||
'from_name' => $config_invoice_from_name,
|
||||
'recipient' => $contact_email,
|
||||
'recipient_name' => $contact_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
]
|
||||
];
|
||||
|
||||
// Email the internal notification address too
|
||||
if (!empty($config_invoice_paid_notification_email)) {
|
||||
$subject = "Payment Received - $client_name - Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello, <br><br>This is a notification that an invoice has been paid in ITFlow. Below is a copy of the receipt sent to the client:-<br><br>--------<br><br>Hello $contact_name,<br><br>We have received online payment for the amount of " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount Paid: " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$data[] = [
|
||||
'from' => $config_invoice_from_email,
|
||||
'from_name' => $config_invoice_from_name,
|
||||
'recipient' => $config_invoice_paid_notification_email,
|
||||
'recipient_name' => $contact_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
}
|
||||
|
||||
$mail = addToMailQueue($mysqli, $data);
|
||||
|
||||
// Email Logging
|
||||
$email_id = mysqli_insert_id($mysqli);
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id");
|
||||
logAction("Invoice", "Payment", "Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id", $client_id, $invoice_id);
|
||||
}
|
||||
|
||||
// Log info
|
||||
$extended_log_desc = '';
|
||||
if (!$pi_livemode) {
|
||||
$extended_log_desc = '(DEV MODE)';
|
||||
}
|
||||
|
||||
// Create Stripe payment gateway fee as an expense (if configured)
|
||||
if ($config_stripe_expense_vendor > 0 && $config_stripe_expense_category > 0) {
|
||||
$gateway_fee = round($invoice_amount * $config_stripe_percentage_fee + $config_stripe_flat_fee, 2);
|
||||
mysqli_query($mysqli,"INSERT INTO expenses SET expense_date = '$pi_date', expense_amount = $gateway_fee, expense_currency_code = '$invoice_currency_code', expense_account_id = $config_stripe_account, expense_vendor_id = $config_stripe_expense_vendor, expense_client_id = $client_id, expense_category_id = $config_stripe_expense_category, expense_description = 'Stripe Transaction for Invoice $invoice_prefix$invoice_number In the Amount of $balance_to_pay', expense_reference = 'Stripe - $pi_id $extended_log_desc'");
|
||||
}
|
||||
|
||||
// Notify/log
|
||||
appNotify("Invoice Paid", "Invoice $invoice_prefix$invoice_number automatically paid", "invoice.php?invoice_id=$invoice_id", $client_id);
|
||||
logAction("Invoice", "Payment", "$session_name initiated Stripe payment amount of " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . " added to invoice $invoice_prefix$invoice_number - $pi_id $extended_log_desc", $client_id, $invoice_id);
|
||||
customAction('invoice_pay', $invoice_id);
|
||||
|
||||
$_SESSION['alert_message'] .= "Payment amount <strong>" . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . "</strong> added";
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
|
||||
} else {
|
||||
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Payment failed', history_description = 'Stripe pay failed due to payment error', history_invoice_id = $invoice_id");
|
||||
logAction("Invoice", "Payment", "Failed online payment amount of invoice $invoice_prefix$invoice_number due to Stripe payment error", $client_id, $invoice_id);
|
||||
$_SESSION['alert_type'] = "error";
|
||||
$_SESSION['alert_message'] = "Payment failed";
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['add_bulk_payment'])) {
|
||||
|
||||
$client_id = intval($_POST['client_id']);
|
||||
|
|
|
|||
197
scripts/cron.php
197
scripts/cron.php
|
|
@ -79,6 +79,15 @@ $currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
|
|||
$config_whitelabel_enabled = intval($row['config_whitelabel_enabled']);
|
||||
$config_whitelabel_key = $row['config_whitelabel_key'];
|
||||
|
||||
// Online Stripe Payment
|
||||
$config_stripe_enable = intval($row['config_stripe_enable']);
|
||||
$config_stripe_secret = $row['config_stripe_secret'];
|
||||
$config_stripe_account = intval($row['config_stripe_account']);
|
||||
$config_stripe_expense_vendor = intval($row['config_stripe_expense_vendor']);
|
||||
$config_stripe_expense_category = intval($row['config_stripe_expense_category']);
|
||||
$config_stripe_percentage_fee = floatval($row['config_stripe_percentage_fee']);
|
||||
$config_stripe_flat_fee = floatval($row['config_stripe_flat_fee']);
|
||||
|
||||
$argv = $_SERVER['argv'];
|
||||
|
||||
// Check cron is enabled
|
||||
|
|
@ -486,7 +495,7 @@ if ($config_send_invoice_reminders == 1) {
|
|||
$subject = "Overdue Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello $contact_name,<br><br>Our records indicate that we have not yet received payment for the invoice $invoice_prefix$invoice_number. We kindly request that you submit your payment as soon as possible. If you have any questions or concerns, please do not hesitate to contact us at $company_email or $company_phone.
|
||||
<br><br>
|
||||
Kindly review the invoice details mentioned below.<br><br>Invoice: $invoice_prefix$invoice_number<br>Issue Date: $invoice_date<br>Total: " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . "<br>Due Date: $invoice_due<br>Over Due By: $day Days<br><br><br>To view your invoice, please click <a href=\'https://$config_base_url/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>here</a>.<br><br><br>--<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
Kindly review the invoice details mentioned below.<br><br>Invoice: $invoice_prefix$invoice_number<br>Issue Date: $invoice_date<br>Total: " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . "<br>Due Date: $invoice_due<br>Over Due By: $day Days<br><br><br>To view your invoice, please click <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>here</a>.<br><br><br>--<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$mail = addToMailQueue($mysqli, [
|
||||
[
|
||||
|
|
@ -595,30 +604,31 @@ while ($row = mysqli_fetch_array($sql_recurring)) {
|
|||
|
||||
mysqli_query($mysqli, "UPDATE recurring SET recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id");
|
||||
|
||||
if ($config_recurring_auto_send_invoice == 1 && $recurring_invoice_email_notify == 1) {
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM invoices
|
||||
// Get details of the newly generated invoice
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM invoices
|
||||
LEFT JOIN clients ON invoice_client_id = client_id
|
||||
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
|
||||
WHERE invoice_id = $new_invoice_id"
|
||||
);
|
||||
);
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$invoice_prefix = sanitizeInput($row['invoice_prefix']);
|
||||
$invoice_number = intval($row['invoice_number']);
|
||||
$invoice_scope = sanitizeInput($row['invoice_scope']);
|
||||
$invoice_date = sanitizeInput($row['invoice_date']);
|
||||
$invoice_due = sanitizeInput($row['invoice_due']);
|
||||
$invoice_amount = floatval($row['invoice_amount']);
|
||||
$invoice_url_key = sanitizeInput($row['invoice_url_key']);
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = sanitizeInput($row['client_name']);
|
||||
$contact_name = sanitizeInput($row['contact_name']);
|
||||
$contact_email = sanitizeInput($row['contact_email']);
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$invoice_prefix = sanitizeInput($row['invoice_prefix']);
|
||||
$invoice_number = intval($row['invoice_number']);
|
||||
$invoice_scope = sanitizeInput($row['invoice_scope']);
|
||||
$invoice_date = sanitizeInput($row['invoice_date']);
|
||||
$invoice_due = sanitizeInput($row['invoice_due']);
|
||||
$invoice_amount = floatval($row['invoice_amount']);
|
||||
$invoice_url_key = sanitizeInput($row['invoice_url_key']);
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = sanitizeInput($row['client_name']);
|
||||
$contact_name = sanitizeInput($row['contact_name']);
|
||||
$contact_email = sanitizeInput($row['contact_email']);
|
||||
if ($config_recurring_auto_send_invoice == 1 && $recurring_invoice_email_notify == 1) {
|
||||
|
||||
$subject = "Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello $contact_name,<br><br>An invoice regarding \"$invoice_scope\" has been generated. Please view the details below.<br><br>Invoice: $invoice_prefix$invoice_number<br>Issue Date: $invoice_date<br>Total: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_currency_code) . "<br>Due Date: $invoice_due<br><br><br>To view your invoice, please click <a href=\'https://$config_base_url/guest_view_invoice.php?invoice_id=$new_invoice_id&url_key=$invoice_url_key\'>here</a>.<br><br><br>--<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
$body = "Hello $contact_name,<br><br>An invoice regarding \"$invoice_scope\" has been generated. Please view the details below.<br><br>Invoice: $invoice_prefix$invoice_number<br>Issue Date: $invoice_date<br>Total: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_currency_code) . "<br>Due Date: $invoice_due<br><br><br>To view your invoice, please click <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$new_invoice_id&url_key=$invoice_url_key\'>here</a>.<br><br><br>--<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$mail = addToMailQueue($mysqli, [
|
||||
[
|
||||
|
|
@ -674,19 +684,150 @@ while ($row = mysqli_fetch_array($sql_recurring)) {
|
|||
|
||||
// Create Payment from Auto Payment
|
||||
if ($recurring_payment_recurring_invoice_id) {
|
||||
mysqli_query($mysqli,"INSERT INTO payments SET payment_date = CURDATE(), payment_amount = $recurring_amount, payment_currency_code = '$recurring_payment_currency_code', payment_account_id = $recurring_payment_account_id, payment_method = '$recurring_payment_method', payment_reference = 'Paid via AutoPay', payment_invoice_id = $new_invoice_id");
|
||||
|
||||
// Get Payment ID for reference
|
||||
$payment_id = mysqli_insert_id($mysqli);
|
||||
if ($recurring_payment_method == "Stripe") {
|
||||
// Stripe payment
|
||||
|
||||
// Update Invoice Status
|
||||
mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Paid' WHERE invoice_id = $new_invoice_id");
|
||||
// Get Stripe info for client
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE client_id = $client_id LIMIT 1"));
|
||||
$stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
$stripe_pm = sanitizeInput($stripe_client_details['stripe_pm']);
|
||||
|
||||
//Add Payment to History
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Paid', history_description = 'Payment added via Auto Pay', history_invoice_id = $new_invoice_id");
|
||||
if ($config_stripe_enable && $stripe_id && $stripe_pm) {
|
||||
|
||||
// Logging
|
||||
logAction("Invoice", "Payment", "Auto Payment amount of " . numfmt_format_currency($currency_format, $recurring_amount, $recurring_payment_currency_code) . " added to invoice $invoice_prefix$invoice_number", $client_id, $new_invoice_id);
|
||||
// Initialize
|
||||
require_once __DIR__ . '/../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
$balance_to_pay = round($invoice_amount, 2);
|
||||
$pi_description = "ITFlow: $client_name payment of $recurring_payment_currency_code $balance_to_pay for $invoice_prefix$invoice_number";
|
||||
|
||||
// Create a payment intent
|
||||
try {
|
||||
$payment_intent = $stripe->paymentIntents->create([
|
||||
'amount' => intval($balance_to_pay * 100), // Times by 100 as Stripe expects values in cents
|
||||
'currency' => $recurring_payment_currency_code,
|
||||
'customer' => $stripe_id,
|
||||
'payment_method' => $stripe_pm,
|
||||
'off_session' => true,
|
||||
'confirm' => true,
|
||||
'description' => $pi_description,
|
||||
'metadata' => [
|
||||
'itflow_client_id' => $client_id,
|
||||
'itflow_client_name' => $client_name,
|
||||
'itflow_invoice_number' => $invoice_prefix . $invoice_number,
|
||||
'itflow_invoice_id' => $new_invoice_id,
|
||||
]
|
||||
]);
|
||||
|
||||
// Get details from PI
|
||||
$pi_id = sanitizeInput($payment_intent->id);
|
||||
$pi_date = date('Y-m-d', $payment_intent->created);
|
||||
$pi_amount_paid = floatval(($payment_intent->amount_received / 100));
|
||||
$pi_currency = strtoupper(sanitizeInput($payment_intent->currency));
|
||||
$pi_livemode = $payment_intent->livemode;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception during payment intent for invoice ID $new_invoice_id / $invoice_prefix$invoice_number: $error");
|
||||
logApp("Stripe", "error", "Exception during PI for invoice ID $new_invoice_id: $error");
|
||||
echo $error;
|
||||
}
|
||||
|
||||
if ($payment_intent->status == "succeeded" && intval($balance_to_pay) == intval($pi_amount_paid)) {
|
||||
|
||||
// Update Invoice Status
|
||||
mysqli_query($mysqli, "UPDATE invoices SET invoice_status = 'Paid' WHERE invoice_id = $new_invoice_id");
|
||||
|
||||
// Add Payment to History
|
||||
mysqli_query($mysqli, "INSERT INTO payments SET payment_date = '$pi_date', payment_amount = $pi_amount_paid, payment_currency_code = '$pi_currency', payment_account_id = $recurring_payment_account_id, payment_method = 'Stripe', payment_reference = 'Stripe - $pi_id', payment_invoice_id = $new_invoice_id");
|
||||
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Paid', history_description = 'Online Payment added (autopay)', history_invoice_id = $new_invoice_id");
|
||||
|
||||
// Email receipt
|
||||
if (!empty($config_smtp_host)) {
|
||||
$subject = "Payment Received - Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello $contact_name,<br><br>We have received online payment for the amount of " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_payment_currency_code) . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$new_invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount Paid: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_payment_currency_code) . "<br><br>Thank you for your business!<br><br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
// Queue Mail
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_invoice_from_email,
|
||||
'from_name' => $config_invoice_from_name,
|
||||
'recipient' => $contact_email,
|
||||
'recipient_name' => $contact_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
]
|
||||
];
|
||||
|
||||
// Email the internal notification address too
|
||||
if (!empty($config_invoice_paid_notification_email)) {
|
||||
$subject = "Payment Received - $client_name - Invoice $invoice_prefix$invoice_number";
|
||||
$body = "Hello, <br><br>This is a notification that an invoice has been paid in ITFlow. Below is a copy of the receipt sent to the client:-<br><br>--------<br><br>Hello $contact_name,<br><br>We have received online payment for the amount of " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_payment_currency_code) . " for invoice <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$new_invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount Paid: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_payment_currency_code) . "<br><br>Thank you for your business!<br><br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$data[] = [
|
||||
'from' => $config_invoice_from_email,
|
||||
'from_name' => $config_invoice_from_name,
|
||||
'recipient' => $config_invoice_paid_notification_email,
|
||||
'recipient_name' => $contact_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
}
|
||||
|
||||
$mail = addToMailQueue($mysqli, $data);
|
||||
|
||||
// Email Logging
|
||||
$email_id = mysqli_insert_id($mysqli);
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $new_invoice_id");
|
||||
logAction("Invoice", "Payment", "Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id", $client_id, $new_invoice_id);
|
||||
}
|
||||
|
||||
// Log info
|
||||
$extended_log_desc = '';
|
||||
if (!$pi_livemode) {
|
||||
$extended_log_desc = '(DEV MODE)';
|
||||
}
|
||||
|
||||
// Create Stripe payment gateway fee as an expense (if configured)
|
||||
if ($config_stripe_expense_vendor > 0 && $config_stripe_expense_category > 0) {
|
||||
$gateway_fee = round($invoice_amount * $config_stripe_percentage_fee + $config_stripe_flat_fee, 2);
|
||||
mysqli_query($mysqli,"INSERT INTO expenses SET expense_date = '$pi_date', expense_amount = $gateway_fee, expense_currency_code = '$invoice_currency_code', expense_account_id = $config_stripe_account, expense_vendor_id = $config_stripe_expense_vendor, expense_client_id = $client_id, expense_category_id = $config_stripe_expense_category, expense_description = 'Stripe Transaction for Invoice $invoice_prefix$invoice_number In the Amount of $balance_to_pay', expense_reference = 'Stripe - $pi_id $extended_log_desc'");
|
||||
}
|
||||
|
||||
// Notify/log
|
||||
appNotify("Invoice Paid", "Invoice $invoice_prefix$invoice_number automatically paid", "invoice.php?invoice_id=$new_invoice_id", $client_id);
|
||||
logAction("Invoice", "Payment", "Auto Stripe payment amount of " . numfmt_format_currency($currency_format, $recurring_amount, $recurring_payment_currency_code) . " added to invoice $invoice_prefix$invoice_number - $pi_id $extended_log_desc", $client_id, $new_invoice_id);
|
||||
customAction('invoice_pay', $new_invoice_id);
|
||||
|
||||
} else {
|
||||
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Payment failed', history_description = 'Stripe autopay failed due to payment error', history_invoice_id = $new_invoice_id");
|
||||
logAction("Invoice", "Payment", "Failed auto Payment amount of invoice $invoice_prefix$invoice_number due to Stripe payment error", $client_id, $new_invoice_id);
|
||||
}
|
||||
|
||||
} else {
|
||||
logAction("Invoice", "Payment", "Failed auto Payment amount of invoice $invoice_prefix$invoice_number due to Stripe configuration error", $client_id, $new_invoice_id);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Else: Cash/Bank payment
|
||||
|
||||
//TODO: Should we send a receipt for auto bank payments, even when nobody actually confirms receipt?
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO payments SET payment_date = CURDATE(), payment_amount = $recurring_amount, payment_currency_code = '$recurring_payment_currency_code', payment_account_id = $recurring_payment_account_id, payment_method = '$recurring_payment_method', payment_reference = 'Paid via AutoPay', payment_invoice_id = $new_invoice_id");
|
||||
|
||||
// Get Payment ID for reference
|
||||
$payment_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Update Invoice Status
|
||||
mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Paid' WHERE invoice_id = $new_invoice_id");
|
||||
|
||||
//Add Payment to History
|
||||
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Paid', history_description = 'Payment added via Auto Pay', history_invoice_id = $new_invoice_id");
|
||||
|
||||
// Logging
|
||||
logAction("Invoice", "Payment", "Auto Payment amount of " . numfmt_format_currency($currency_format, $recurring_amount, $recurring_payment_currency_code) . " added to invoice $invoice_prefix$invoice_number", $client_id, $new_invoice_id);
|
||||
}
|
||||
|
||||
} //End Auto Payment
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue