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