dolibarr 21.0.3
ipn.php
1<?php
2/* Copyright (C) 2018-2020 Thibault FOUCART <support@ptibogxiv.net>
3 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2023 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21if (!defined('NOLOGIN')) {
22 define("NOLOGIN", 1); // This means this output page does not require to be logged.
23}
24if (!defined('NOCSRFCHECK')) {
25 define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
26}
27if (!defined('NOIPCHECK')) {
28 define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
29}
30if (!defined('NOBROWSERNOTIF')) {
31 define('NOBROWSERNOTIF', '1');
32}
33
34// Because 2 entities can have the same ref.
35$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1));
36if (is_numeric($entity)) {
37 define("DOLENTITY", $entity);
38}
39
40// So log file will have a suffix
41if (!defined('USESUFFIXINLOG')) {
42 define('USESUFFIXINLOG', '_stripeipn');
43}
44
45// Load Dolibarr environment
46require '../../main.inc.php';
47require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
48require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
49require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php';
50require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
51require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
52require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
53require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
54require_once DOL_DOCUMENT_ROOT.'/compta/prelevement/class/bonprelevement.class.php';
55require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
56require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
57require_once DOL_DOCUMENT_ROOT.'/includes/stripe/stripe-php/init.php';
58require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php';
65// You can find your endpoint's secret in your webhook settings
66if (GETPOSTISSET('connect')) {
67 if (GETPOSTISSET('test')) {
68 $endpoint_secret = getDolGlobalString('STRIPE_TEST_WEBHOOK_CONNECT_KEY');
69 $service = 'StripeTest';
70 $servicestatus = 0;
71 } else {
72 $endpoint_secret = getDolGlobalString('STRIPE_LIVE_WEBHOOK_CONNECT_KEY');
73 $service = 'StripeLive';
74 $servicestatus = 1;
75 }
76} else {
77 if (GETPOSTISSET('test')) {
78 $endpoint_secret = getDolGlobalString('STRIPE_TEST_WEBHOOK_KEY');
79 $service = 'StripeTest';
80 $servicestatus = 0;
81 } else {
82 $endpoint_secret = getDolGlobalString('STRIPE_LIVE_WEBHOOK_KEY');
83 $service = 'StripeLive';
84 $servicestatus = 1;
85 }
86}
87
88if (!isModEnabled('stripe')) {
89 httponly_accessforbidden('Module Stripe not enabled');
90}
91
92if (empty($endpoint_secret)) {
93 httponly_accessforbidden('Error: Setup of module Stripe not complete for mode '.dol_escape_htmltag($service).'. The WEBHOOK_KEY is not defined.', 400, 1);
94}
95
96if (getDolGlobalString('STRIPE_USER_ACCOUNT_FOR_ACTIONS')) {
97 // We set the user to use for all ipn actions in Dolibarr
98 $user = new User($db);
99 $user->fetch(getDolGlobalString('STRIPE_USER_ACCOUNT_FOR_ACTIONS'));
100 $user->loadRights();
101} else {
102 httponly_accessforbidden('Error: Setup of module Stripe not complete for mode '.dol_escape_htmltag($service).'. The STRIPE_USER_ACCOUNT_FOR_ACTIONS is not defined.', 400, 1);
103}
104
105$now = dol_now();
106
107// Security
108// The test on security key is done later into constructEvent() method.
109
110
111/*
112 * Actions
113 */
114
115$payload = @file_get_contents("php://input");
116$sig_header = empty($_SERVER["HTTP_STRIPE_SIGNATURE"]) ? '' : $_SERVER["HTTP_STRIPE_SIGNATURE"];
117$event = null;
118
119if (getDolGlobalString('STRIPE_DEBUG')) {
120 $fh = fopen(DOL_DATA_ROOT.'/dolibarr_stripeipn_payload.log', 'w+');
121 if ($fh) {
122 fwrite($fh, dol_print_date(dol_now('gmt'), 'standard').' IPN Called. service='.$service.' HTTP_STRIPE_SIGNATURE='.$sig_header."\n");
123 fwrite($fh, $payload);
124 fclose($fh);
125 dolChmod(DOL_DATA_ROOT.'/dolibarr_stripeipn_payload.log');
126 }
127}
128
129$error = 0;
130
131try {
132 $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
133} catch (UnexpectedValueException $e) {
134 // Invalid payload
135 httponly_accessforbidden('Invalid payload', 400);
136} catch (\Stripe\Exception\SignatureVerificationException $e) {
137 httponly_accessforbidden('Invalid signature. May be a hook for an event created by another Stripe env ? Check setup of your keys whsec_...', 400);
138} catch (Exception $e) {
139 httponly_accessforbidden('Error '.$e->getMessage(), 400);
140}
141
142// Do something with $event
143
144$langs->load("main");
145
146
147if (isModEnabled('multicompany') && !empty($conf->stripeconnect->enabled) && is_object($mc)) {
148 $sql = "SELECT entity";
149 $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
150 $sql .= " WHERE service = '".$db->escape($service)."' and tokenstring LIKE '%".$db->escape($db->escapeforlike($event->account))."%'";
151
152 dol_syslog(get_class($db)."::fetch", LOG_DEBUG);
153 dol_syslog(get_class($db)."::fetch", LOG_DEBUG, 0, '_payment');
154 $result = $db->query($sql);
155 if ($result) {
156 if ($db->num_rows($result)) {
157 $obj = $db->fetch_object($result);
158 $key = $obj->entity;
159 } else {
160 $key = 1;
161 }
162 } else {
163 $key = 1;
164 }
165 $ret = $mc->switchEntity($key);
166}
167
168// list of action
169$stripe = new Stripe($db);
170
171// Subject
172$societeName = getDolGlobalString('MAIN_INFO_SOCIETE_NOM');
173if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
174 $societeName = getDolGlobalString('MAIN_APPLICATION_TITLE');
175}
176
178
179dol_syslog("***** Stripe IPN was called with event->type=".$event->type." service=".$service);
180dol_syslog("***** Stripe IPN was called with event->type=".$event->type." service=".$service, LOG_DEBUG, 0, '_payment');
181
182
183if ($event->type == 'payout.created' && getDolGlobalString('STRIPE_AUTO_RECORD_PAYOUT')) {
184 // When a payout is created by Stripe to transfer money to your account
185 dol_syslog("object = ".var_export($event->data, true));
186 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
187
188 $error = 0;
189
190 $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", date('Y-m-d H:i:s', $event->data->object->arrival_date), 'chaine', 0, '', $conf->entity);
191
192 if ($result > 0) {
193 $subject = '['.$societeName.'] Notification - Stripe payout scheduled';
194 if (!empty($user->email)) {
195 $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">";
196 } else {
197 $sendto = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL') . '" <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>';
198 }
199 $replyto = $sendto;
200 $sendtocc = '';
201 if (getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL')) {
202 $sendtocc = getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL') . '" <' . getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL').'>';
203 }
204
205 $message = "A bank transfer of ".price2num($event->data->object->amount / 100)." ".$event->data->object->currency." should arrive in your account the ".dol_print_date($event->data->object->arrival_date, 'dayhour');
206
207 $mailfile = new CMailFile(
208 $subject,
209 $sendto,
210 $replyto,
211 $message,
212 array(),
213 array(),
214 array(),
215 $sendtocc,
216 '',
217 0,
218 -1
219 );
220
221 $ret = $mailfile->sendfile();
222
223 return 1;
224 } else {
225 $error++;
226 http_response_code(500);
227 return -1;
228 }
229} elseif ($event->type == 'payout.paid' && getDolGlobalString('STRIPE_AUTO_RECORD_PAYOUT')) {
230 // When a payout to transfer money to your account is completely done
231 dol_syslog("object = ".var_export($event->data, true));
232 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
233
234 $error = 0;
235 $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", 0, 'chaine', 0, '', $conf->entity);
236 if ($result) {
237 $langs->load("errors");
238
239 $dateo = dol_now();
240 $label = $event->data->object->description;
241 $amount = $event->data->object->amount / 100;
242 $amount_to = $event->data->object->amount / 100;
243 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
244
245 $accountfrom = new Account($db);
246 $accountfrom->fetch(getDolGlobalInt('STRIPE_BANK_ACCOUNT_FOR_PAYMENTS'));
247
248 $accountto = new Account($db);
249 $accountto->fetch(getDolGlobalInt('STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS'));
250
251 if (($accountto->id != $accountfrom->id) && empty($error)) {
252 $bank_line_id_from = 0;
253 $bank_line_id_to = 0;
254 $result = 0;
255
256 // By default, electronic transfer from bank to bank
257 $typefrom = 'PRE';
258 $typeto = 'VIR';
259
260 $db->begin();
261
262 if (!$error) {
263 $bank_line_id_from = $accountfrom->addline($dateo, $typefrom, $label, -1 * (float) price2num($amount), '', '', $user);
264 }
265 if (!($bank_line_id_from > 0)) {
266 $error++;
267 }
268 if (!$error) {
269 $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount), '', '', $user);
270 }
271 if (!($bank_line_id_to > 0)) {
272 $error++;
273 }
274
275 if (!$error) {
276 $result = $accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, DOL_URL_ROOT.'/compta/bank/line.php?rowid=', '(banktransfert)', 'banktransfert');
277 }
278 if (!($result > 0)) {
279 $error++;
280 }
281 if (!$error) {
282 $result = $accountto->add_url_line($bank_line_id_to, $bank_line_id_from, DOL_URL_ROOT.'/compta/bank/line.php?rowid=', '(banktransfert)', 'banktransfert');
283 }
284 if (!($result > 0)) {
285 $error++;
286 }
287
288 if (!$error) {
289 $db->commit();
290 } else {
291 $db->rollback();
292 }
293
294 // Send email
295 if (!$error) {
296 $subject = '['.$societeName.'] - NotificationOTIFICATION] Stripe payout done';
297 if (!empty($user->email)) {
298 $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">";
299 } else {
300 $sendto = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL') . '" <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>';
301 }
302 $replyto = $sendto;
303 $sendtocc = '';
304 if (getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL')) {
305 $sendtocc = getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL') . '" <' . getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL').'>';
306 }
307
308 $message = "A bank transfer of ".price2num($event->data->object->amount / 100)." ".$event->data->object->currency." has been done to your account the ".dol_print_date($event->data->object->arrival_date, 'dayhour');
309
310 $mailfile = new CMailFile(
311 $subject,
312 $sendto,
313 $replyto,
314 $message,
315 array(),
316 array(),
317 array(),
318 $sendtocc,
319 '',
320 0,
321 -1
322 );
323
324 $ret = $mailfile->sendfile();
325 }
326 }
327
328 return 1;
329 } else {
330 $error++;
331 http_response_code(500);
332 return -1;
333 }
334} elseif ($event->type == 'customer.source.created') {
335 //TODO: save customer's source
336} elseif ($event->type == 'customer.source.updated') {
337 //TODO: update customer's source
338} elseif ($event->type == 'customer.source.delete') {
339 //TODO: delete customer's source
340} elseif ($event->type == 'customer.deleted') {
341 // When a customer account is delete on Stripe side
342 $db->begin();
343 $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_account WHERE key_account = '".$db->escape($event->data->object->id)."' AND site = 'stripe'";
344 $db->query($sql);
345 $db->commit();
346} elseif ($event->type == 'payment_intent.succeeded') {
347 // Called when making payment with PaymentIntent method ($conf->global->STRIPE_USE_NEW_CHECKOUT is on).
348 dol_syslog("object = ".var_export($event->data, true));
349 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
350
351 include_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php';
352 global $stripearrayofkeysbyenv;
353 $error = 0;
354 $object = $event->data->object;
355 $TRANSACTIONID = $object->id; // Example pi_123456789...
356 $ipaddress = $object->metadata->ipaddress;
357 $now = dol_now();
358 $currencyCodeType = strtoupper($object->currency);
359 $paymentmethodstripeid = $object->payment_method;
360 $customer_id = $object->customer;
361 $invoice_id = "";
362 $paymentTypeCode = ""; // payment type according to Stripe
363 $paymentTypeCodeInDolibarr = ""; // payment type according to Dolibarr
364 $payment_amount = 0;
365 $payment_amountInDolibarr = 0;
366
367 dol_syslog("Try to find a payment in database for the payment_intent id = ".$TRANSACTIONID);
368 dol_syslog("Try to find a payment in database for the payment_intent id = ".$TRANSACTIONID, LOG_DEBUG, 0, '_payment');
369
370 $sql = "SELECT pi.rowid, pi.fk_facture, pi.fk_prelevement_bons, pi.amount, pi.type, pi.traite";
371 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
372 $sql .= " WHERE pi.ext_payment_id = '".$db->escape($TRANSACTIONID)."'";
373 $sql .= " AND pi.ext_payment_site = '".$db->escape($service)."'";
374
375 $result = $db->query($sql);
376 if ($result) {
377 $obj = $db->fetch_object($result);
378 if ($obj) {
379 if ($obj->type == 'ban') {
380 $pdid = $obj->rowid;
381 if ($obj->traite == 1) {
382 // This is a direct-debit with an order (llx_bon_prelevement) ALREADY generated, so
383 // it means we received here the confirmation that payment request is finished.
384 $invoice_id = $obj->fk_facture;
385 $directdebitorcreditransfer_id = $obj->fk_prelevement_bons;
386 $payment_amountInDolibarr = $obj->amount;
387 $paymentTypeCodeInDolibarr = $obj->type;
388
389 dol_syslog("Found a request in database to pay with direct debit generated (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")");
390 dol_syslog("Found a request in database to pay with direct debit generated (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")", LOG_DEBUG, 0, '_payment');
391 } else {
392 dol_syslog("Found a request in database not yet generated (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id."). Was the order deleted after being sent ?", LOG_WARNING);
393 dol_syslog("Found a request in database not yet generated (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id."). Was the order deleted after being sent ?", LOG_WARNING, 0, '_payment');
394 }
395 }
396 if ($obj->type == 'card' || empty($obj->type)) {
397 $pdid = $obj->rowid;
398 if ($obj->traite == 0) {
399 // This is a card payment not already flagged as sent to Stripe.
400 $invoice_id = $obj->fk_facture;
401 $payment_amountInDolibarr = $obj->amount;
402 $paymentTypeCodeInDolibarr = empty($obj->type) ? 'card' : $obj->type;
403
404 dol_syslog("Found a request in database to pay with card (pdid = ".$pdid."). We should fix status traite to 1");
405 dol_syslog("Found a request in database to pay with card (pdid = ".$pdid."). We should fix status traite to 1", LOG_DEBUG, 0, '_payment');
406 } else {
407 dol_syslog("Found a request in database to pay with card (pdid = ".$pdid.") already set to traite=1. Nothing to fix.");
408 dol_syslog("Found a request in database to pay with card (pdid = ".$pdid.") already set to traite=1. Nothing to fix.", LOG_DEBUG, 0, '_payment');
409 }
410 }
411 } else {
412 dol_syslog("Payment intent ".$TRANSACTIONID." not found into database, so ignored.");
413 dol_syslog("Payment intent ".$TRANSACTIONID." not found into database, so ignored.", LOG_DEBUG, 0, '_payment');
414 http_response_code(200);
415 print "Payment intent ".$TRANSACTIONID." not found into database, so ignored.";
416 return 1;
417 }
418 } else {
419 http_response_code(500);
420 print $db->lasterror();
421 return -1;
422 }
423
424 if ($paymentTypeCodeInDolibarr) {
425 // Here, we need to do something. A $invoice_id has been found.
426
427 $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
428
429 dol_syslog("Get the Stripe payment object for the payment method id = ".json_encode($paymentmethodstripeid));
430 dol_syslog("Get the Stripe payment object for the payment method id = ".json_encode($paymentmethodstripeid), LOG_DEBUG, 0, '_payment');
431
432 $s = new \Stripe\StripeClient($stripeacc);
433
434 $paymentmethodstripe = $s->paymentMethods->retrieve($paymentmethodstripeid);
435 $paymentTypeCode = $paymentmethodstripe->type;
436 if ($paymentTypeCode == "ban" || $paymentTypeCode == "sepa_debit") {
437 $paymentTypeCode = "PRE";
438 } elseif ($paymentTypeCode == "card") {
439 $paymentTypeCode = "CB";
440 }
441
442 $payment_amount = $payment_amountInDolibarr;
443 // TODO Check payment_amount in Stripe (received) is same than the one in Dolibarr
444
445 $postactionmessages = array();
446
447 if ($paymentTypeCode == "CB" && ($paymentTypeCodeInDolibarr == 'card' || empty($paymentTypeCodeInDolibarr))) {
448 // Case payment type in Stripe and into prelevement_demande are both CARD.
449 // For this case, payment should already have been recorded so we just update flag of payment request if not yet 1
450
451 // TODO Set traite to 1
452 dol_syslog("TODO update flag traite to 1");
453 dol_syslog("TODO update flag traite to 1", LOG_DEBUG, 0, '_payment');
454 } elseif ($paymentTypeCode == "PRE" && $paymentTypeCodeInDolibarr == 'ban') {
455 // Case payment type in Stripe and into prelevement_demande are both BAN.
456 // For this case, payment on invoice (not yet recorded) must be done and direct debit order must be closed.
457
458 $paiement = new Paiement($db);
459 $paiement->datepaye = $now;
460 $paiement->date = $now;
461 if ($currencyCodeType == $conf->currency) {
462 $paiement->amounts = [$invoice_id => $payment_amount]; // Array with all payments dispatching with invoice id
463 } else {
464 $paiement->multicurrency_amounts = [$invoice_id => $payment_amount]; // Array with all payments dispatching
465
466 $postactionmessages[] = 'Payment was done in a currency ('.$currencyCodeType.') other than the expected currency of company ('.$conf->currency.')';
467 $ispostactionok = -1;
468 // Not yet supported, so error
469 $error++;
470 }
471
472 // Get ID of payment PRE
473 $paiement->paiementcode = $paymentTypeCode;
474 $sql = "SELECT id FROM ".MAIN_DB_PREFIX."c_paiement";
475 $sql .= " WHERE code = '".$db->escape($paymentTypeCode)."'";
476 $sql .= " AND entity IN (".getEntity('c_paiement').")";
477 $resql = $db->query($sql);
478 if ($resql) {
479 $obj = $db->fetch_object($resql);
480 $paiement->paiementid = $obj->id;
481 } else {
482 $error++;
483 }
484
485 $paiement->num_payment = '';
486 $paiement->note_public = '';
487 $paiement->note_private = 'StripeSepa payment received by IPN webhook - ' . dol_print_date($now, 'standard') . ' (TZ server) using servicestatus=' . $servicestatus . ($ipaddress ? ' from ip ' . $ipaddress : '') . ' - Transaction ID = ' . $TRANSACTIONID;
488 $paiement->ext_payment_id = $TRANSACTIONID.':'.$customer_id.'@'.$stripearrayofkeysbyenv[$servicestatus]['publishable_key']; // May be we should store py_... instead of pi_... but we started with pi_... so we continue.
489 $paiement->ext_payment_site = $service;
490
491 $ispaymentdone = 0;
492 $sql = "SELECT p.rowid FROM ".MAIN_DB_PREFIX."paiement as p";
493 $sql .= " WHERE p.ext_payment_id = '".$db->escape($paiement->ext_payment_id)."'";
494 $sql .= " AND p.ext_payment_site = '".$db->escape($paiement->ext_payment_site)."'";
495 $result = $db->query($sql);
496 if ($result) {
497 if ($db->num_rows($result)) {
498 $ispaymentdone = 1;
499 dol_syslog('* Payment for ext_payment_id '.$paiement->ext_payment_id.' already done. We do not recreate the payment');
500 dol_syslog('* Payment for ext_payment_id '.$paiement->ext_payment_id.' already done. We do not recreate the payment', LOG_DEBUG, 0, '_payment');
501 }
502 }
503
504 $db->begin();
505
506 if (!$error && !$ispaymentdone) {
507 dol_syslog('* Record payment type PRE for invoice id ' . $invoice_id . '. It includes closing of invoice and regenerating document.');
508 dol_syslog('* Record payment type PRE for invoice id ' . $invoice_id . '. It includes closing of invoice and regenerating document.', LOG_DEBUG, 0, '_payment');
509
510 // This include closing invoices to 'paid' (and trigger including unsuspending) and regenerating document
511 $paiement_id = $paiement->create($user, 1);
512 if ($paiement_id < 0) {
513 $postactionmessages[] = $paiement->error . ($paiement->error ? ' ' : '') . implode("<br>\n", $paiement->errors);
514 $ispostactionok = -1;
515 $error++;
516
517 dol_syslog("Failed to create the payment for invoice id " . $invoice_id);
518 dol_syslog("Failed to create the payment for invoice id " . $invoice_id, LOG_DEBUG, 0, '_payment');
519 } else {
520 $postactionmessages[] = 'Payment created';
521
522 dol_syslog("The payment has been created for invoice id " . $invoice_id);
523 dol_syslog("The payment has been created for invoice id " . $invoice_id, LOG_DEBUG, 0, '_payment');
524 }
525 }
526
527 if (!$error && isModEnabled('bank')) {
528 // Search again the payment to see if it is already linked to a bank payment record (We should always find the payment that was created before).
529 $ispaymentdone = 0;
530 $sql = "SELECT p.rowid, p.fk_bank FROM ".MAIN_DB_PREFIX."paiement as p";
531 $sql .= " WHERE p.ext_payment_id = '".$db->escape($paiement->ext_payment_id)."'";
532 $sql .= " AND p.ext_payment_site = '".$db->escape($paiement->ext_payment_site)."'";
533 $sql .= " AND p.fk_bank <> 0";
534 $result = $db->query($sql);
535 if ($result) {
536 if ($db->num_rows($result)) {
537 $ispaymentdone = 1;
538 $obj = $db->fetch_object($result);
539 dol_syslog('* Payment already linked to bank record '.$obj->fk_bank.' . We do not recreate the link');
540 dol_syslog('* Payment already linked to bank record '.$obj->fk_bank.' . We do not recreate the link', LOG_DEBUG, 0, '_payment');
541 }
542 }
543 if (!$ispaymentdone) {
544 dol_syslog('* Add payment to bank');
545 dol_syslog('* Add payment to bank', LOG_DEBUG, 0, '_payment');
546
547 // The bank used is the one defined into Stripe setup
548 $paymentmethod = 'stripe';
549 $bankaccountid = getDolGlobalInt("STRIPE_BANK_ACCOUNT_FOR_PAYMENTS");
550
551 if ($bankaccountid > 0) {
552 $label = '(CustomerInvoicePayment)';
553 $result = $paiement->addPaymentToBank($user, 'payment', $label, $bankaccountid, $customer_id, '');
554 if ($result < 0) {
555 $postactionmessages[] = $paiement->error . ($paiement->error ? ' ' : '') . implode("<br>\n", $paiement->errors);
556 $ispostactionok = -1;
557 $error++;
558 } else {
559 $postactionmessages[] = 'Bank transaction of payment created (by ipn.php file)';
560 }
561 } else {
562 $postactionmessages[] = 'Setup of bank account to use in module ' . $paymentmethod . ' was not set. No way to record the payment.';
563 $ispostactionok = -1;
564 $error++;
565 }
566 }
567 }
568
569 if (!$error && isModEnabled('prelevement')) {
570 $bon = new BonPrelevement($db);
571 $idbon = 0;
572 $sql = "SELECT dp.fk_prelevement_bons as idbon";
573 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as dp";
574 $sql .= " JOIN ".MAIN_DB_PREFIX."prelevement_bons as pb"; // Here we join to prevent modification of a prelevement bon already credited
575 $sql .= " ON pb.rowid = dp.fk_prelevement_bons";
576 $sql .= " WHERE dp.fk_facture = ".((int) $invoice_id);
577 $sql .= " AND dp.sourcetype = 'facture'";
578 $sql .= " AND dp.ext_payment_id = '".$db->escape($TRANSACTIONID)."'";
579 $sql .= " AND dp.traite = 1";
580 $sql .= " AND statut = ".((int) $bon::STATUS_TRANSFERED); // To be sure that it's not already credited
581 $result = $db->query($sql);
582 if ($result) {
583 if ($db->num_rows($result)) {
584 $obj = $db->fetch_object($result);
585 $idbon = $obj->idbon;
586 dol_syslog('* Prelevement must be set to credited');
587 dol_syslog('* Prelevement must be set to credited', LOG_DEBUG, 0, '_payment');
588 } else {
589 dol_syslog('* Prelevement not found or already credited');
590 dol_syslog('* Prelevement not found or already credited', LOG_DEBUG, 0, '_payment');
591 }
592 } else {
593 $postactionmessages[] = $db->lasterror();
594 $ispostactionok = -1;
595 $error++;
596 }
597
598 if (!$error && !empty($idbon)) {
599 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_bons";
600 $sql .= " SET fk_user_credit = ".((int) $user->id);
601 $sql .= ", statut = ".((int) $bon::STATUS_CREDITED);
602 $sql .= ", date_credit = '".$db->idate($now)."'";
603 $sql .= ", credite = 1";
604 $sql .= " WHERE rowid = ".((int) $idbon);
605 $sql .= " AND statut = ".((int) $bon::STATUS_TRANSFERED);
606
607 $result = $db->query($sql);
608 if (!$result) {
609 $postactionmessages[] = $db->lasterror();
610 $ispostactionok = -1;
611 $error++;
612 }
613 }
614
615 if (!$error && !empty($idbon)) {
616 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_lignes";
617 $sql .= " SET statut = 2";
618 $sql .= " WHERE fk_prelevement_bons = ".((int) $idbon);
619 $result = $db->query($sql);
620 if (!$result) {
621 $postactionmessages[] = $db->lasterror();
622 $ispostactionok = -1;
623 $error++;
624 }
625 }
626 }
627
628 if (!$error) {
629 $db->commit();
630 http_response_code(200);
631 return 1;
632 } else {
633 $db->rollback();
634 http_response_code(500);
635 return -1;
636 }
637 } else {
638 dol_syslog("The payment mode of this payment is ".$paymentTypeCode." in Stripe and ".$paymentTypeCodeInDolibarr." in Dolibarr. This case is not managed by the IPN");
639 dol_syslog("The payment mode of this payment is ".$paymentTypeCode." in Stripe and ".$paymentTypeCodeInDolibarr." in Dolibarr. This case is not managed by the IPN", LOG_DEBUG, 0, '_payment');
640 }
641 } else {
642 dol_syslog("Nothing to do in database because we don't know paymentTypeIdInDolibarr");
643 dol_syslog("Nothing to do in database because we don't know paymentTypeIdInDolibarr", LOG_DEBUG, 0, '_payment');
644 }
645} elseif ($event->type == 'payment_intent.payment_failed') {
646 // When a try to take payment has failed. Useful for asynchronous SEPA payment that fails.
647 dol_syslog("A try to make a payment has failed");
648 dol_syslog("A try to make a payment has failed", LOG_DEBUG, 0, '_payment');
649
650 $object = $event->data->object;
651 $ipaddress = $object->metadata->ipaddress;
652 $currencyCodeType = strtoupper($object->currency);
653 $paymentmethodstripeid = $object->payment_method;
654 $customer_id = $object->customer;
655
656 $chargesdataarray = array();
657 $objpayid = '';
658 $objpaydesc = '';
659 $objinvoiceid = 0;
660 $objerrcode = '';
661 $objerrmessage = '';
662 $objpaymentmodetype = '';
663 if (!empty($object->charges)) { // Old format
664 $chargesdataarray = $object->charges->data;
665 foreach ($chargesdataarray as $chargesdata) {
666 $objpayid = $chargesdata->id;
667 $objpaydesc = $chargesdata->description;
668 $objinvoiceid = 0;
669 if ($chargesdata->metadata->dol_type == 'facture') {
670 $objinvoiceid = $chargesdata->metadata->dol_id;
671 }
672 $objerrcode = $chargesdata->outcome->reason;
673 $objerrmessage = $chargesdata->outcome->seller_message;
674
675 $objpaymentmodetype = $chargesdata->payment_method_details->type;
676 break;
677 }
678 }
679 if (!empty($object->last_payment_error)) { // New format 2023-10-16
680 // $object is probably an object of type Stripe\PaymentIntent
681 $objpayid = $object->latest_charge;
682 $objpaydesc = $object->description;
683 $objinvoiceid = 0;
684 if ($object->metadata->dol_type == 'facture') {
685 $objinvoiceid = $object->metadata->dol_id;
686 }
687 $objerrcode = empty($object->last_payment_error->code) ? $object->last_payment_error->decline_code : $object->last_payment_error->code;
688 $objerrmessage = $object->last_payment_error->message;
689
690 $objpaymentmodetype = $object->last_payment_error->payment_method->type;
691 }
692
693 dol_syslog("objpayid=".$objpayid." objpaymentmodetype=".$objpaymentmodetype." objerrcode=".$objerrcode);
694 dol_syslog("objpayid=".$objpayid." objpaymentmodetype=".$objpaymentmodetype." objerrcode=".$objerrcode, LOG_DEBUG, 0, '_payment');
695
696 // If this is a differed payment for SEPA, add a line into agenda events
697 if ($objpaymentmodetype == 'sepa_debit') {
698 $db->begin();
699
700 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
701 $actioncomm = new ActionComm($db);
702
703 if ($objinvoiceid > 0) {
704 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
705 $invoice = new Facture($db);
706 $invoice->fetch($objinvoiceid);
707
708 $actioncomm->userownerid = 0;
709 $actioncomm->percentage = -1;
710
711 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
712 $actioncomm->code = 'AC_IPN';
713
714 $actioncomm->datep = $now;
715 $actioncomm->datef = $now;
716
717 $actioncomm->socid = $invoice->socid;
718 $actioncomm->fk_project = $invoice->fk_project;
719 $actioncomm->fk_element = $invoice->id;
720 $actioncomm->elementtype = 'invoice';
721 $actioncomm->ip = getUserRemoteIP();
722 }
723
724 $actioncomm->note_private = 'Error returned on payment id '.$objpayid.' after SEPA payment request '.$objpaydesc.'<br>Error code is: '.$objerrcode.'<br>Error message is: '.$objerrmessage;
725 $actioncomm->label = 'Payment error (SEPA Stripe)';
726
727 $result = $actioncomm->create($user);
728 if ($result <= 0) {
729 dol_syslog($actioncomm->error, LOG_ERR);
730 dol_syslog($actioncomm->error, LOG_ERR, 0, '_payment');
731 $error++;
732 }
733
734 if (! $error) {
735 $db->commit();
736 } else {
737 $db->rollback();
738 http_response_code(500);
739 return -1;
740 }
741 }
742} elseif ($event->type == 'checkout.session.completed') { // Called when making payment with new Checkout method ($conf->global->STRIPE_USE_NEW_CHECKOUT is on).
743 // TODO: create fees
744} elseif ($event->type == 'payment_method.attached') {
745 dol_syslog("object = ".var_export($event->data, true));
746 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
747
748 // When we link a payment method with a customer on Stripe side
749 require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
750 require_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
751 $societeaccount = new SocieteAccount($db);
752
753 $companypaymentmode = new CompanyPaymentMode($db);
754
755 $idthirdparty = $societeaccount->getThirdPartyID($db->escape($event->data->object->customer), 'stripe', $servicestatus);
756 if ($idthirdparty > 0) {
757 // If the payment mode attached is to a stripe account owned by an external customer in societe_account (so a thirdparty that has a Stripe account),
758 // we can create the payment mode
759 $companypaymentmode->stripe_card_ref = $event->data->object->id;
760 $companypaymentmode->fk_soc = $idthirdparty;
761 $companypaymentmode->bank = null;
762 $companypaymentmode->label = '';
763 $companypaymentmode->number = $event->data->object->id;
764 $companypaymentmode->last_four = $event->data->object->card->last4;
765 $companypaymentmode->card_type = $event->data->object->card->branding;
766
767 $companypaymentmode->owner_name = $event->data->object->billing_details->name;
768 $companypaymentmode->proprio = $companypaymentmode->owner_name; // We may still need this formodulebuilder because name of field is "proprio"
769
770 $companypaymentmode->exp_date_month = (int) $event->data->object->card->exp_month;
771 $companypaymentmode->exp_date_year = (int) $event->data->object->card->exp_year;
772 $companypaymentmode->cvn = null;
773 $companypaymentmode->datec = $event->data->object->created;
774 $companypaymentmode->default_rib = 0;
775 $companypaymentmode->type = $event->data->object->type;
776 $companypaymentmode->country_code = $event->data->object->card->country;
777 $companypaymentmode->status = $servicestatus;
778
779 // TODO Check that a payment mode $companypaymentmode->stripe_card_ref does not exists yet to avoid to create duplicates
780 // so we can remove the test on STRIPE_NO_DUPLICATE_CHECK
781 if (getDolGlobalString('STRIPE_NO_DUPLICATE_CHECK')) {
782 $db->begin();
783 $result = $companypaymentmode->create($user);
784 if ($result < 0) {
785 $error++;
786 }
787 if (!$error) {
788 $db->commit();
789 } else {
790 $db->rollback();
791 http_response_code(500);
792 return -1;
793 }
794 }
795 }
796} elseif ($event->type == 'payment_method.updated') {
797 dol_syslog("object = ".var_export($event->data, true));
798 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
799
800 // When we update a payment method on Stripe side
801 require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
802 $companypaymentmode = new CompanyPaymentMode($db);
803 $companypaymentmode->fetch(0, '', 0, '', " AND stripe_card_ref = '".$db->escape($event->data->object->id)."'");
804 if ($companypaymentmode->id > 0) {
805 // If we found a payment mode with the ID
806 $companypaymentmode->bank = null;
807 $companypaymentmode->label = '';
808 $companypaymentmode->number = $db->escape($event->data->object->id);
809 $companypaymentmode->last_four = $db->escape($event->data->object->card->last4);
810 $companypaymentmode->proprio = $db->escape($event->data->object->billing_details->name); // deprecated
811 $companypaymentmode->owner_name = $db->escape($event->data->object->billing_details->name);
812 $companypaymentmode->exp_date_month = (int) $event->data->object->card->exp_month;
813 $companypaymentmode->exp_date_year = (int) $event->data->object->card->exp_year;
814 $companypaymentmode->cvn = null;
815 $companypaymentmode->datec = (int) $event->data->object->created;
816 $companypaymentmode->default_rib = 0;
817 $companypaymentmode->type = $db->escape($event->data->object->type);
818 $companypaymentmode->country_code = $db->escape($event->data->object->card->country);
819 $companypaymentmode->status = $servicestatus;
820
821 $db->begin();
822 if (!$error) {
823 $result = $companypaymentmode->update($user);
824 if ($result < 0) {
825 $error++;
826 }
827 }
828 if (!$error) {
829 $db->commit();
830 } else {
831 $db->rollback();
832 }
833 }
834} elseif ($event->type == 'payment_method.detached') {
835 // When we remove a payment method on Stripe side
836 $db->begin();
837 $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_rib WHERE number = '".$db->escape($event->data->object->id)."' and status = ".((int) $servicestatus);
838 $db->query($sql);
839 $db->commit();
840} elseif ($event->type == 'charge.succeeded') {
841 // Deprecated. TODO: create fees and redirect to paymentok.php
842} elseif ($event->type == 'charge.failed') {
843 // Deprecated. TODO: Redirect to paymentko.php
844} elseif (($event->type == 'source.chargeable') && ($event->data->object->type == 'three_d_secure') && ($event->data->object->three_d_secure->authenticated == true)) {
845 // Deprecated.
846} elseif ($event->type == 'charge.dispute.closed') {
847 // When a dispute to cancel a SEPA payment is finished
848 dol_syslog("object = ".var_export($event->data, true));
849 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
850} elseif ($event->type == 'charge.dispute.funds_withdrawn') {
851 // When a dispute/withdraw to cancel a SEPA payment is done
852 dol_syslog("object = ".var_export($event->data, true));
853 dol_syslog("object = ".var_export($event->data, true), LOG_DEBUG, 0, '_payment');
854
855 global $stripearrayofkeysbyenv;
856 $error = 0;
857 $errormsg = '';
858 $object = $event->data->object;
859 $TRANSACTIONID = $object->payment_intent;
860 $ipaddress = $object->metadata->ipaddress;
861 $now = dol_now();
862 $currencyCodeType = strtoupper($object->currency);
863 $paymentmethodstripeid = $object->payment_method;
864 $customer_id = $object->customer;
865 $reason = $object->reason;
866 $amountdisputestripe = $object->amoutndispute; // In stripe format
867 $amountdispute = $amountdisputestripe; // In real currency format
868
869 $invoice_id = "";
870 $paymentTypeCode = ""; // payment type according to Stripe
871 $paymentTypeCodeInDolibarr = ""; // payment type according to Dolibarr
872 $payment_amount = 0;
873 $payment_amountInDolibarr = 0;
874
875 dol_syslog("Try to find the payment in database for the payment_intent id = ".$TRANSACTIONID);
876 dol_syslog("Try to find the payment in database for the payment_intent id = ".$TRANSACTIONID, LOG_DEBUG, 0, '_payment');
877
878 $sql = "SELECT pi.rowid, pi.fk_facture, pi.fk_prelevement_bons, pi.amount, pi.type, pi.traite";
879 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
880 $sql .= " WHERE pi.ext_payment_id = '".$db->escape($TRANSACTIONID)."'";
881 $sql .= " AND pi.ext_payment_site = '".$db->escape($service)."'";
882
883 $result = $db->query($sql);
884 if ($result) {
885 $obj = $db->fetch_object($result);
886 if ($obj) {
887 if ($obj->type == 'ban') {
888 // This is a direct-debit with an order (llx_bon_prelevement).
889 $pdid = $obj->rowid;
890 $invoice_id = $obj->fk_facture;
891 $directdebitorcreditransfer_id = $obj->fk_prelevement_bons;
892 $payment_amountInDolibarr = $obj->amount;
893 $paymentTypeCodeInDolibarr = $obj->type;
894
895 dol_syslog("Found the payment intent for ban in database (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")");
896 dol_syslog("Found the payment intent for ban in database (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")", LOG_DEBUG, 0, '_payment');
897 }
898 if ($obj->type == 'card' || empty($obj->type)) {
899 // This is a card payment.
900 $pdid = $obj->rowid;
901 $invoice_id = $obj->fk_facture;
902 $directdebitorcreditransfer_id = 0;
903 $payment_amountInDolibarr = $obj->amount;
904 $paymentTypeCodeInDolibarr = empty($obj->type) ? 'card' : $obj->type;
905
906 dol_syslog("Found the payment intent for card in database (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")");
907 dol_syslog("Found the payment intent for card in database (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")", LOG_DEBUG, 0, '_payment');
908 }
909 } else {
910 dol_syslog("Payment intent ".$TRANSACTIONID." not found into database, so ignored.");
911 dol_syslog("Payment intent ".$TRANSACTIONID." not found into database, so ignored.", LOG_DEBUG, 0, '_payment');
912 http_response_code(200);
913 print "Payment intent ".$TRANSACTIONID." not found into database, so ignored.";
914 return 1;
915 }
916 } else {
917 http_response_code(500);
918 print $db->lasterror();
919 return -1;
920 }
921
922 dol_syslog("objinvoiceid=".$invoice_id);
923 dol_syslog("objinvoiceid=".$invoice_id, LOG_DEBUG, 0, '_payment');
924 $tmpinvoice = new Facture($db);
925 $tmpinvoice->fetch($invoice_id);
926 $tmpinvoice->fetch_thirdparty();
927
928 dol_syslog("The payment disputed is ".$amountdispute." and the invoice is ".$payment_amountInDolibarr);
929 dol_syslog("The payment disputed is ".$amountdispute." and the invoice is ".$payment_amountInDolibarr, LOG_DEBUG, 0, '_payment');
930
931 if ($amountdispute != $payment_amountInDolibarr) {
932 http_response_code(500);
933 print "The payment disputed is ".$amountdispute." and the invoice is ".$payment_amountInDolibarr.". Amount differs, we don't know what to do.";
934 return -1;
935 }
936
937 $accountfrom = new Account($db);
938 $accountfrom->fetch(getDolGlobalInt('STRIPE_BANK_ACCOUNT_FOR_PAYMENTS'));
939
940 // Now we add a negative payment
941 $paiement = new Paiement($db);
942
943 $amounts = array();
944 $amounts[$tmpinvoice->id] = -1 * $payment_amountInDolibarr;
945
946 $paiement->datepaye = dol_now();
947 $paiement->amounts = $amounts; // Array with all payments dispatching with invoice id
948 /*$paiement->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
949 $paiement->multicurrency_code = $multicurrency_code; // Array with all currency of payments dispatching
950 $paiement->multicurrency_tx = $multicurrency_tx; // Array with all currency tx of payments dispatching
951 */
952 $paiement->paiementid = dol_getIdFromCode($db, 'PRE', 'c_paiement', 'code', 'id', 1);
953 $paiement->num_payment = $object->id; // A string like 'du_...'
954 $paiement->note_public = 'Fund withdrawn by bank. Reason: '.$reason;
955 $paiement->note_private = '';
956 $paiement->fk_account = $accountfrom->id;
957
958 $db->begin();
959
960 $alreadytransferedinaccounting = $tmpinvoice->getVentilExportCompta();
961
962 if ($alreadytransferedinaccounting) {
963 // TODO Test if invoice already in accountancy.
964 // What to do ?
965 $errormsg = 'Error: the invoice '.$tmpinvoice->id.' is already transferred into accounting. Don\'t know what to do.';
966 $error++;
967 }
968
969 if (! $error && $tmpinvoice->status == Facture::STATUS_CLOSED) {
970 // Switch back the invoice to status validated
971 $result = $tmpinvoice->setStatut(Facture::STATUS_VALIDATED);
972 if ($result < 0) {
973 $errormsg = $tmpinvoice->error.implode(', ', $tmpinvoice->errors);
974 $error++;
975 }
976 }
977
978 if (! $error) {
979 $paiement_id = $paiement->create($user, 0, $tmpinvoice->thirdparty); // This include regenerating documents
980 if ($paiement_id < 0) {
981 $errormsg = $paiement->error.implode(', ', $paiement->errors);
982 $error++;
983 }
984 }
985
986 if (!$error) {
987 //$db->commit(); // Code not yet enough tested
988 $db->rollback();
989 http_response_code(500);
990 return -1;
991 } else {
992 $db->rollback();
993 http_response_code(500);
994 print $errormsg;
995 return -1;
996 }
997}
998
999
1000// End of page. Default return HTTP code will be 200
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
Class to manage bank accounts.
Class to manage agenda events (actions)
Class to manage withdrawal receipts.
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Class for CompanyPaymentMode.
Class to manage invoices.
const STATUS_VALIDATED
Validated (need to be paid)
const STATUS_CLOSED
Classified paid.
Class to manage payments of customer invoices.
Class for SocieteAccount.
Stripe class @TODO No reason to extends CommonObject.
Class to manage Dolibarr users.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
httponly_accessforbidden($message='1', $http_response_code=403, $stringalreadysanitized=0)
Show a message to say access is forbidden and stop program.