dolibarr 19.0.3
ipn.php
1<?php
2/* Copyright (C) 2018-2020 Thibault FOUCART <support@ptibogxiv.net>
3 * Copyright (C) 2018 Fédéric France <frederic.france@netlogic.fr>
4 * Copyright (C) 2023 Laurent Destailleur <eldy@users.sourceforge.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20if (!defined('NOLOGIN')) {
21 define("NOLOGIN", 1); // This means this output page does not require to be logged.
22}
23if (!defined('NOCSRFCHECK')) {
24 define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
25}
26if (!defined('NOIPCHECK')) {
27 define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
28}
29if (!defined('NOBROWSERNOTIF')) {
30 define('NOBROWSERNOTIF', '1');
31}
32
33$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1));
34if (is_numeric($entity)) {
35 define("DOLENTITY", $entity);
36}
37
38// So log file will have a suffix
39if (!defined('USESUFFIXINLOG')) {
40 define('USESUFFIXINLOG', '_stripeipn');
41}
42
43// Load Dolibarr environment
44require '../../main.inc.php';
45require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
46require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
47require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php';
48require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
49require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
50require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
51require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
52require_once DOL_DOCUMENT_ROOT.'/compta/prelevement/class/bonprelevement.class.php';
53require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
54require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
55require_once DOL_DOCUMENT_ROOT.'/includes/stripe/stripe-php/init.php';
56require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php';
57
58
59// You can find your endpoint's secret in your webhook settings
60if (isset($_GET['connect'])) {
61 if (isset($_GET['test'])) {
62 $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_CONNECT_KEY;
63 $service = 'StripeTest';
64 $servicestatus = 0;
65 } else {
66 $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_CONNECT_KEY;
67 $service = 'StripeLive';
68 $servicestatus = 1;
69 }
70} else {
71 if (isset($_GET['test'])) {
72 $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_KEY;
73 $service = 'StripeTest';
74 $servicestatus = 0;
75 } else {
76 $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_KEY;
77 $service = 'StripeLive';
78 $servicestatus = 1;
79 }
80}
81
82if (!isModEnabled('stripe')) {
83 httponly_accessforbidden('Module Stripe not enabled');
84}
85
86if (empty($endpoint_secret)) {
87 httponly_accessforbidden('Error: Setup of module Stripe not complete for mode '.dol_escape_htmltag($service).'. The WEBHOOK_KEY is not defined.', 400, 1);
88}
89
90if (getDolGlobalString('STRIPE_USER_ACCOUNT_FOR_ACTIONS')) {
91 // We set the user to use for all ipn actions in Dolibarr
92 $user = new User($db);
93 $user->fetch($conf->global->STRIPE_USER_ACCOUNT_FOR_ACTIONS);
94 $user->getrights();
95} else {
96 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);
97}
98
99$now = dol_now();
100
101// Security
102// The test on security key is done later into constructEvent() method.
103
104
105/*
106 * Actions
107 */
108
109$payload = @file_get_contents("php://input");
110$sig_header = empty($_SERVER["HTTP_STRIPE_SIGNATURE"]) ? '' : $_SERVER["HTTP_STRIPE_SIGNATURE"];
111$event = null;
112
113if (getDolGlobalString('STRIPE_DEBUG')) {
114 $fh = fopen(DOL_DATA_ROOT.'/dolibarr_stripeipn_payload.log', 'w+');
115 if ($fh) {
116 fwrite($fh, dol_print_date(dol_now('gmt'), 'standard').' IPN Called. service='.$service.' HTTP_STRIPE_SIGNATURE='.$sig_header."\n");
117 fwrite($fh, $payload);
118 fclose($fh);
119 dolChmod(DOL_DATA_ROOT.'/dolibarr_stripeipn_payload.log');
120 }
121}
122
123$error = 0;
124
125try {
126 $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
127} catch (UnexpectedValueException $e) {
128 // Invalid payload
129 httponly_accessforbidden('Invalid payload', 400);
130} catch (\Stripe\Exception\SignatureVerificationException $e) {
131 httponly_accessforbidden('Invalid signature. May be a hook for an event created by another Stripe env ? Check setup of your keys whsec_...', 400);
132} catch (Exception $e) {
133 httponly_accessforbidden('Error '.$e->getMessage(), 400);
134}
135
136// Do something with $event
137
138$langs->load("main");
139
140
141if (isModEnabled('multicompany') && !empty($conf->stripeconnect->enabled) && is_object($mc)) {
142 $sql = "SELECT entity";
143 $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
144 $sql .= " WHERE service = '".$db->escape($service)."' and tokenstring LIKE '%".$db->escape($db->escapeforlike($event->account))."%'";
145
146 dol_syslog(get_class($db)."::fetch", LOG_DEBUG);
147 $result = $db->query($sql);
148 if ($result) {
149 if ($db->num_rows($result)) {
150 $obj = $db->fetch_object($result);
151 $key = $obj->entity;
152 } else {
153 $key = 1;
154 }
155 } else {
156 $key = 1;
157 }
158 $ret = $mc->switchEntity($key);
159}
160
161// list of action
162$stripe = new Stripe($db);
163
164// Subject
165$societeName = $conf->global->MAIN_INFO_SOCIETE_NOM;
166if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
167 $societeName = $conf->global->MAIN_APPLICATION_TITLE;
168}
169
171
172dol_syslog("***** Stripe IPN was called with event->type=".$event->type." service=".$service);
173
174
175if ($event->type == 'payout.created') {
176 $error = 0;
177
178 $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", date('Y-m-d H:i:s', $event->data->object->arrival_date), 'chaine', 0, '', $conf->entity);
179
180 if ($result > 0) {
181 $subject = $societeName.' - [NOTIFICATION] Stripe payout scheduled';
182 if (!empty($user->email)) {
183 $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">";
184 } else {
185 $sendto = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL') . '" <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>';
186 }
187 $replyto = $sendto;
188 $sendtocc = '';
189 if (getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL')) {
190 $sendtocc = getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL') . '" <' . getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL').'>';
191 }
192
193 $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');
194
195 $mailfile = new CMailFile(
196 $subject,
197 $sendto,
198 $replyto,
199 $message,
200 array(),
201 array(),
202 array(),
203 $sendtocc,
204 '',
205 0,
206 -1
207 );
208
209 $ret = $mailfile->sendfile();
210
211 return 1;
212 } else {
213 $error++;
214 http_response_code(500);
215 return -1;
216 }
217} elseif ($event->type == 'payout.paid') {
218 $error = 0;
219 $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", null, 'chaine', 0, '', $conf->entity);
220 if ($result) {
221 $langs->load("errors");
222
223 $dateo = dol_now();
224 $label = $event->data->object->description;
225 $amount = $event->data->object->amount / 100;
226 $amount_to = $event->data->object->amount / 100;
227 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
228
229 $accountfrom = new Account($db);
230 $accountfrom->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS);
231
232 $accountto = new Account($db);
233 $accountto->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS);
234
235 if (($accountto->id != $accountfrom->id) && empty($error)) {
236 $bank_line_id_from = 0;
237 $bank_line_id_to = 0;
238 $result = 0;
239
240 // By default, electronic transfert from bank to bank
241 $typefrom = 'PRE';
242 $typeto = 'VIR';
243
244 if (!$error) {
245 $bank_line_id_from = $accountfrom->addline($dateo, $typefrom, $label, -1 * price2num($amount), '', '', $user);
246 }
247 if (!($bank_line_id_from > 0)) {
248 $error++;
249 }
250 if (!$error) {
251 $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount), '', '', $user);
252 }
253 if (!($bank_line_id_to > 0)) {
254 $error++;
255 }
256
257 if (!$error) {
258 $result = $accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, DOL_URL_ROOT.'/compta/bank/line.php?rowid=', '(banktransfert)', 'banktransfert');
259 }
260 if (!($result > 0)) {
261 $error++;
262 }
263 if (!$error) {
264 $result = $accountto->add_url_line($bank_line_id_to, $bank_line_id_from, DOL_URL_ROOT.'/compta/bank/line.php?rowid=', '(banktransfert)', 'banktransfert');
265 }
266 if (!($result > 0)) {
267 $error++;
268 }
269 }
270
271 $subject = $societeName.' - [NOTIFICATION] Stripe payout done';
272 if (!empty($user->email)) {
273 $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">";
274 } else {
275 $sendto = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL') . '" <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>';
276 }
277 $replyto = $sendto;
278 $sendtocc = '';
279 if (getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL')) {
280 $sendtocc = getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL') . '" <' . getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL').'>';
281 }
282
283 $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');
284
285 $mailfile = new CMailFile(
286 $subject,
287 $sendto,
288 $replyto,
289 $message,
290 array(),
291 array(),
292 array(),
293 $sendtocc,
294 '',
295 0,
296 -1
297 );
298
299 $ret = $mailfile->sendfile();
300
301 return 1;
302 } else {
303 $error++;
304 http_response_code(500);
305 return -1;
306 }
307} elseif ($event->type == 'customer.source.created') {
308 //TODO: save customer's source
309} elseif ($event->type == 'customer.source.updated') {
310 //TODO: update customer's source
311} elseif ($event->type == 'customer.source.delete') {
312 //TODO: delete customer's source
313} elseif ($event->type == 'customer.deleted') {
314 $db->begin();
315 $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_account WHERE key_account = '".$db->escape($event->data->object->id)."' and site='stripe'";
316 $db->query($sql);
317 $db->commit();
318} elseif ($event->type == 'payment_intent.succeeded') { // Called when making payment with PaymentIntent method ($conf->global->STRIPE_USE_NEW_CHECKOUT is on).
319 //dol_syslog("object = ".var_export($event->data, true));
320 include_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php';
321 global $stripearrayofkeysbyenv;
322 $error = 0;
323 $object = $event->data->object;
324 $TRANSACTIONID = $object->id; // Example pi_123456789...
325 $ipaddress = $object->metadata->ipaddress;
326 $now = dol_now();
327 $currencyCodeType = strtoupper($object->currency);
328 $paymentmethodstripeid = $object->payment_method;
329 $customer_id = $object->customer;
330 $invoice_id = "";
331 $paymentTypeId = ""; // payment type according to Stripe
332 $paymentTypeIdInDolibarr = ""; // payment type according to Dolibarr
333 $payment_amount = 0;
334 $payment_amountInDolibarr = 0;
335
336 dol_syslog("Try to find a payment in database for the payment_intent id = ".$TRANSACTIONID);
337
338 $sql = "SELECT pi.rowid, pi.fk_facture, pi.fk_prelevement_bons, pi.amount, pi.type, pi.traite";
339 $sql .= " FROM llx_prelevement_demande as pi";
340 $sql .= " WHERE pi.ext_payment_id = '".$db->escape($TRANSACTIONID)."'";
341 $sql .= " AND pi.ext_payment_site = '".$db->escape($service)."'";
342
343 $result = $db->query($sql);
344 if ($result) {
345 $obj = $db->fetch_object($result);
346 if ($obj) {
347 if ($obj->type == 'ban') {
348 if ($obj->traite == 1) {
349 // This is a direct-debit with an order (llx_bon_prelevement) ALREADY generated, so
350 // it means we received here the confirmation that payment request is finished.
351 $pdid = $obj->rowid;
352 $invoice_id = $obj->fk_facture;
353 $directdebitorcreditransfer_id = $obj->fk_prelevement_bons;
354 $payment_amountInDolibarr = $obj->amount;
355 $paymentTypeIdInDolibarr = $obj->type;
356
357 dol_syslog("Found a request in database to pay with direct debit generated (pdid = ".$pdid." directdebitorcreditransfer_id=".$directdebitorcreditransfer_id.")");
358 } else {
359 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);
360 }
361 }
362 if ($obj->type == 'card' || empty($obj->type)) {
363 if ($obj->traite == 0) {
364 // This is a card payment not already flagged as sent to Stripe.
365 $pdid = $obj->rowid;
366 $invoice_id = $obj->fk_facture;
367 $payment_amountInDolibarr = $obj->amount;
368 $paymentTypeIdInDolibarr = empty($obj->type) ? 'card' : $obj->type;
369
370 dol_syslog("Found a request in database to pay with card (pdid = ".$pdid."). We should fix status traite to 1");
371 } else {
372 dol_syslog("Found a request in database to pay with card (pdid = ".$pdid.") already set to traite=1. Nothing to fix.");
373 }
374 }
375 } else {
376 dol_syslog("Payment intent ".$TRANSACTIONID." not found into database, so ignored.");
377 http_response_code(200);
378 print "Payment intent ".$TRANSACTIONID." not found into database, so ignored.";
379 return 1;
380 }
381 } else {
382 http_response_code(500);
383 print $db->lasterror();
384 return -1;
385 }
386
387 if ($paymentTypeIdInDolibarr) {
388 // Here, we need to do something. A $invoice_id has been found.
389
390 $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
391
392 dol_syslog("Get the Stripe payment object for the payment method id = ".json_encode($paymentmethodstripeid));
393
394 $s = new \Stripe\StripeClient($stripeacc);
395
396 $paymentmethodstripe = $s->paymentMethods->retrieve($paymentmethodstripeid);
397 $paymentTypeId = $paymentmethodstripe->type;
398 if ($paymentTypeId == "ban" || $paymentTypeId == "sepa_debit") {
399 $paymentTypeId = "PRE";
400 } elseif ($paymentTypeId == "card") {
401 $paymentTypeId = "CB";
402 }
403
404 $payment_amount = $payment_amountInDolibarr;
405 // TODO Check payment_amount in Stripe (received) is same than the one in Dolibarr
406
407 if ($paymentTypeId == "CB" && ($paymentTypeIdInDolibarr == 'card' || empty($paymentTypeIdInDolibarr))) {
408 // Case payment type in Stripe and into prelevement_demande are both CARD.
409 // For this case, payment should already have been recorded so we just update flag of payment request if not yet 1
410
411 // TODO Set traite to 1
412 dol_syslog("TODO update flag traite to 1");
413 } elseif ($paymentTypeId == "PRE" && $paymentTypeIdInDolibarr == 'ban') {
414 // Case payment type in Stripe and into prelevement_demande are both BAN.
415 // For this case, payment on invoice (not yet recorded) must be done and direct debit order must be closed.
416
417 $paiement = new Paiement($db);
418 $paiement->datepaye = $now;
419 $paiement->date = $now;
420 if ($currencyCodeType == $conf->currency) {
421 $paiement->amounts = [$invoice_id => $payment_amount]; // Array with all payments dispatching with invoice id
422 } else {
423 $paiement->multicurrency_amounts = [$invoice_id => $payment_amount]; // Array with all payments dispatching
424
425 $postactionmessages[] = 'Payment was done in a different currency than currency expected of company';
426 $ispostactionok = -1;
427 // Not yet supported, so error
428 $error++;
429 }
430 $paiement->paiementid = $paymentTypeId;
431 $paiement->num_payment = '';
432 $paiement->note_public = '';
433 $paiement->note_private = 'StripeSepa payment ' . dol_print_date($now, 'standard') . ' using ' . $servicestatus . ($ipaddress ? ' from ip ' . $ipaddress : '') . ' - Transaction ID = ' . $TRANSACTIONID;
434 $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.
435 $paiement->ext_payment_site = $service;
436
437 $ispaymentdone = 0;
438 $sql = "SELECT p.rowid FROM llx_paiement as p";
439 $sql .= " WHERE p.ext_payment_id = '".$db->escape($paiement->ext_payment_id)."'";
440 $sql .= " AND p.ext_payment_site = '".$db->escape($paiement->ext_payment_site)."'";
441 $result = $db->query($sql);
442 if ($result) {
443 if ($db->num_rows($result)) {
444 $ispaymentdone = 1;
445 dol_syslog('* Payment for ext_payment_id '.$paiement->ext_payment_id.' already done. We do not recreate the payment');
446 }
447 }
448
449 $db->begin();
450
451 if (!$error && !$ispaymentdone) {
452 dol_syslog('* Record payment for invoice id ' . $invoice_id . '. It includes closing of invoice and regenerating document');
453
454 // This include closing invoices to 'paid' (and trigger including unsuspending) and regenerating document
455 $paiement_id = $paiement->create($user, 1);
456 if ($paiement_id < 0) {
457 $postactionmessages[] = $paiement->error . ($paiement->error ? ' ' : '') . join("<br>\n", $paiement->errors);
458 $ispostactionok = -1;
459 $error++;
460
461 dol_syslog("Failed to create the payment for invoice id " . $invoice_id);
462 } else {
463 $postactionmessages[] = 'Payment created';
464
465 dol_syslog("The payment has been created for invoice id " . $invoice_id);
466 }
467 }
468
469 if (!$error && isModEnabled('banque')) {
470 // Search again the payment to see if it is already linked to a bank payment record (We should always find the payement now we have created before).
471 $ispaymentdone = 0;
472 $sql = "SELECT p.rowid, p.fk_bank FROM llx_paiement as p";
473 $sql .= " WHERE p.ext_payment_id = '".$db->escape($paiement->ext_payment_id)."'";
474 $sql .= " AND p.ext_payment_site = '".$db->escape($paiement->ext_payment_site)."'";
475 $sql .= " AND p.fk_bank <> 0";
476 $result = $db->query($sql);
477 if ($result) {
478 if ($db->num_rows($result)) {
479 $ispaymentdone = 1;
480 $obj = $db->fetch_object($result);
481 dol_syslog('* Payment already linked to bank record '.$obj->fk_bank.' . We do not recreate the link');
482 }
483 }
484 if (!$ispaymentdone) {
485 dol_syslog('* Add payment to bank');
486
487 // The bank used is the one defined into Stripe setup
488 $paymentmethod = 'stripe';
489 $bankaccountid = getDolGlobalInt("STRIPE_BANK_ACCOUNT_FOR_PAYMENTS");
490
491 if ($bankaccountid > 0) {
492 $label = '(CustomerInvoicePayment)';
493 $result = $paiement->addPaymentToBank($user, 'payment', $label, $bankaccountid, $customer_id, '');
494 if ($result < 0) {
495 $postactionmessages[] = $paiement->error . ($paiement->error ? ' ' : '') . join("<br>\n", $paiement->errors);
496 $ispostactionok = -1;
497 $error++;
498 } else {
499 $postactionmessages[] = 'Bank transaction of payment created (by ipn.php file)';
500 }
501 } else {
502 $postactionmessages[] = 'Setup of bank account to use in module ' . $paymentmethod . ' was not set. No way to record the payment.';
503 $ispostactionok = -1;
504 $error++;
505 }
506 }
507 }
508
509 if (!$error && isModEnabled('prelevement')) {
510 $bon = new BonPrelevement($db);
511 $idbon = 0;
512 $sql = "SELECT dp.fk_prelevement_bons as idbon";
513 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as dp";
514 $sql .= " JOIN ".MAIN_DB_PREFIX."prelevement_bons as pb"; // Here we join to prevent modification of a prelevement bon already credited
515 $sql .= " ON pb.rowid = dp.fk_prelevement_bons";
516 $sql .= " WHERE dp.fk_facture = ".((int) $invoice_id);
517 $sql .= " AND dp.sourcetype = 'facture'";
518 $sql .= " AND dp.ext_payment_id = '".$db->escape($TRANSACTIONID)."'";
519 $sql .= " AND dp.traite = 1";
520 $sql .= " AND statut = ".((int) $bon::STATUS_TRANSFERED); // To be sure that it's not already credited
521 $result = $db->query($sql);
522 if ($result) {
523 if ($db->num_rows($result)) {
524 $obj = $db->fetch_object($result);
525 $idbon = $obj->idbon;
526 dol_syslog('* Prelevement must be set to credited');
527 } else {
528 dol_syslog('* Prelevement not found or already credited');
529 }
530 } else {
531 $postactionmessages[] = $db->lasterror();
532 $ispostactionok = -1;
533 $error++;
534 }
535
536 if (!$error && !empty($idbon)) {
537 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_bons";
538 $sql .= " SET fk_user_credit = ".((int) $user->id);
539 $sql .= ", statut = ".((int) $bon::STATUS_CREDITED);
540 $sql .= ", date_credit = '".$db->idate($now)."'";
541 $sql .= ", credite = 1";
542 $sql .= " WHERE rowid = ".((int) $idbon);
543 $sql .= " AND statut = ".((int) $bon::STATUS_TRANSFERED);
544
545 $result = $db->query($sql);
546 if (!$result) {
547 $postactionmessages[] = $db->lasterror();
548 $ispostactionok = -1;
549 $error++;
550 }
551 }
552
553 if (!$error && !empty($idbon)) {
554 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_lignes";
555 $sql .= " SET statut = 2";
556 $sql .= " WHERE fk_prelevement_bons = ".((int) $idbon);
557 $result = $db->query($sql);
558 if (!$result) {
559 $postactionmessages[] = $db->lasterror();
560 $ispostactionok = -1;
561 $error++;
562 }
563 }
564 }
565
566 if (!$error) {
567 $db->commit();
568 http_response_code(200);
569 return 1;
570 } else {
571 $db->rollback();
572 http_response_code(500);
573 return -1;
574 }
575 } else {
576 dol_syslog("The payment mode of this payment is ".$paymentTypeId." in Stripe and ".$paymentTypeIdInDolibarr." in Dolibarr. This case is not managed by the IPN");
577 }
578 } else {
579 dol_syslog("Nothing to do in database because we don't know paymentTypeIdInDolibarr");
580 }
581} elseif ($event->type == 'payment_intent.payment_failed') {
582 dol_syslog("A try to make a payment has failed");
583
584 $object = $event->data->object;
585 $ipaddress = $object->metadata->ipaddress;
586 $currencyCodeType = strtoupper($object->currency);
587 $paymentmethodstripeid = $object->payment_method;
588 $customer_id = $object->customer;
589
590 $chargesdataarray = array();
591 $objpayid = '';
592 $objpaydesc = '';
593 $objinvoiceid = 0;
594 $objerrcode = '';
595 $objerrmessage = '';
596 $objpaymentmodetype = '';
597 if (!empty($object->charges)) { // Old format
598 $chargesdataarray = $object->charges->data;
599 foreach ($chargesdataarray as $chargesdata) {
600 $objpayid = $chargesdata->id;
601 $objpaydesc = $chargesdata->description;
602 $objinvoiceid = 0;
603 if ($chargesdata->metadata->dol_type == 'facture') {
604 $objinvoiceid = $chargesdata->metadata->dol_id;
605 }
606 $objerrcode = $chargesdata->outcome->reason;
607 $objerrmessage = $chargesdata->outcome->seller_message;
608
609 $objpaymentmodetype = $chargesdata->payment_method_details->type;
610 break;
611 }
612 }
613 if (!empty($object->last_payment_error)) { // New format 2023-10-16
614 // $object is probably an object of type Stripe\PaymentIntent
615 $objpayid = $object->latest_charge;
616 $objpaydesc = $object->description;
617 $objinvoiceid = 0;
618 if ($object->metadata->dol_type == 'facture') {
619 $objinvoiceid = $object->metadata->dol_id;
620 }
621 $objerrcode = empty($object->last_payment_error->code) ? $object->last_payment_error->decline_code : $object->last_payment_error->code;
622 $objerrmessage = $object->last_payment_error->message;
623
624 $objpaymentmodetype = $object->last_payment_error->payment_method->type;
625 }
626
627 dol_syslog("objpayid=".$objpayid." objpaymentmodetype=".$objpaymentmodetype." objerrcode=".$objerrcode);
628
629 // If this is a differed payment for SEPA, add a line into agenda events
630 if ($objpaymentmodetype == 'sepa_debit') {
631 $db->begin();
632
633 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
634 $actioncomm = new ActionComm($db);
635
636 if ($objinvoiceid > 0) {
637 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
638 $invoice = new Facture($db);
639 $invoice->fetch($objinvoiceid);
640
641 $actioncomm->userownerid = 0;
642 $actioncomm->percentage = -1;
643
644 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
645 $actioncomm->code = 'AC_IPN';
646
647 $actioncomm->datep = $now;
648 $actioncomm->datef = $now;
649
650 $actioncomm->socid = $invoice->socid;
651 $actioncomm->fk_project = $invoice->fk_project;
652 $actioncomm->fk_element = $invoice->id;
653 $actioncomm->elementtype = 'invoice';
654 $actioncomm->ip = getUserRemoteIP();
655 }
656
657 $actioncomm->note_private = 'Error returned on payment id '.$objpayid.' after SEPA payment request '.$objpaydesc.'<br>Error code is: '.$objerrcode.'<br>Error message is: '.$objerrmessage;
658 $actioncomm->label = 'Payment error (SEPA Stripe)';
659
660 $result = $actioncomm->create($user);
661 if ($result <= 0) {
662 dol_syslog($actioncomm->error, LOG_ERR);
663 $error++;
664 }
665
666 if (! $error) {
667 $db->commit();
668 } else {
669 $db->rollback();
670 http_response_code(500);
671 return -1;
672 }
673 }
674} elseif ($event->type == 'checkout.session.completed') { // Called when making payment with new Checkout method ($conf->global->STRIPE_USE_NEW_CHECKOUT is on).
675 // TODO: create fees
676} elseif ($event->type == 'payment_method.attached') {
677 require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
678 require_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
679 $societeaccount = new SocieteAccount($db);
680
681 $companypaymentmode = new CompanyPaymentMode($db);
682
683 $idthirdparty = $societeaccount->getThirdPartyID($db->escape($event->data->object->customer), 'stripe', $servicestatus);
684 if ($idthirdparty > 0) {
685 // 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),
686 // we can create the payment mode
687 $companypaymentmode->stripe_card_ref = $db->escape($event->data->object->id);
688 $companypaymentmode->fk_soc = $idthirdparty;
689 $companypaymentmode->bank = null;
690 $companypaymentmode->label = null;
691 $companypaymentmode->number = $db->escape($event->data->object->id);
692 $companypaymentmode->last_four = $db->escape($event->data->object->card->last4);
693 $companypaymentmode->card_type = $db->escape($event->data->object->card->branding);
694 $companypaymentmode->proprio = $db->escape($event->data->object->billing_details->name);
695 $companypaymentmode->exp_date_month = $db->escape($event->data->object->card->exp_month);
696 $companypaymentmode->exp_date_year = $db->escape($event->data->object->card->exp_year);
697 $companypaymentmode->cvn = null;
698 $companypaymentmode->datec = $db->escape($event->data->object->created);
699 $companypaymentmode->default_rib = 0;
700 $companypaymentmode->type = $db->escape($event->data->object->type);
701 $companypaymentmode->country_code = $db->escape($event->data->object->card->country);
702 $companypaymentmode->status = $servicestatus;
703
704 // TODO Check that a payment mode $companypaymentmode->stripe_card_ref does not exists yet to avoid to create duplicates
705 // so we can remove the test on STRIPE_NO_DUPLICATE_CHECK
706 if (getDolGlobalString('STRIPE_NO_DUPLICATE_CHECK')) {
707 $db->begin();
708 $result = $companypaymentmode->create($user);
709 if ($result < 0) {
710 $error++;
711 }
712 if (!$error) {
713 $db->commit();
714 } else {
715 $db->rollback();
716 }
717 }
718 }
719} elseif ($event->type == 'payment_method.updated') {
720 require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
721 $companypaymentmode = new CompanyPaymentMode($db);
722 $companypaymentmode->fetch(0, '', 0, '', " AND stripe_card_ref = '".$db->escape($event->data->object->id)."'");
723 if ($companypaymentmode->id > 0) {
724 // If we found a payment mode with the ID
725 $companypaymentmode->bank = null;
726 $companypaymentmode->label = null;
727 $companypaymentmode->number = $db->escape($event->data->object->id);
728 $companypaymentmode->last_four = $db->escape($event->data->object->card->last4);
729 $companypaymentmode->proprio = $db->escape($event->data->object->billing_details->name);
730 $companypaymentmode->exp_date_month = $db->escape($event->data->object->card->exp_month);
731 $companypaymentmode->exp_date_year = $db->escape($event->data->object->card->exp_year);
732 $companypaymentmode->cvn = null;
733 $companypaymentmode->datec = $db->escape($event->data->object->created);
734 $companypaymentmode->default_rib = 0;
735 $companypaymentmode->type = $db->escape($event->data->object->type);
736 $companypaymentmode->country_code = $db->escape($event->data->object->card->country);
737 $companypaymentmode->status = $servicestatus;
738
739 $db->begin();
740 if (!$error) {
741 $result = $companypaymentmode->update($user);
742 if ($result < 0) {
743 $error++;
744 }
745 }
746 if (!$error) {
747 $db->commit();
748 } else {
749 $db->rollback();
750 }
751 }
752} elseif ($event->type == 'payment_method.detached') {
753 $db->begin();
754 $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_rib WHERE number = '".$db->escape($event->data->object->id)."' and status = ".((int) $servicestatus);
755 $db->query($sql);
756 $db->commit();
757} elseif ($event->type == 'charge.succeeded') {
758 // Deprecated. TODO: create fees and redirect to paymentok.php
759} elseif ($event->type == 'charge.failed') {
760 // Deprecated. TODO: Redirect to paymentko.php
761} elseif (($event->type == 'source.chargeable') && ($event->data->object->type == 'three_d_secure') && ($event->data->object->three_d_secure->authenticated == true)) {
762 // Deprecated.
763}
764
765// End of page. Default return HTTP code will be 200
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.
Class to manage payments of customer invoices.
Class for SocieteAccount.
Stripe class.
Class to manage Dolibarr users.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
getUserRemoteIP()
Return the IP of remote user.
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.
httponly_accessforbidden($message=1, $http_response_code=403, $stringalreadysanitized=0)
Show a message to say access is forbidden and stop program.