dolibarr  16.0.5
ipn.php
1 <?php
2 /* Copyright (C) 2018-2020 Thibault FOUCART <support@ptibogxiv.net>
3  * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 if (!defined('NOLOGIN')) {
20  define("NOLOGIN", 1); // This means this output page does not require to be logged.
21 }
22 if (!defined('NOCSRFCHECK')) {
23  define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
24 }
25 if (!defined('NOIPCHECK')) {
26  define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
27 }
28 if (!defined('NOBROWSERNOTIF')) {
29  define('NOBROWSERNOTIF', '1');
30 }
31 
32 $entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1));
33 if (is_numeric($entity)) {
34  define("DOLENTITY", $entity);
35 }
36 
37 require '../../main.inc.php';
38 require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
39 require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
40 require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
43 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
44 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
45 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
46 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
47 
48 require_once DOL_DOCUMENT_ROOT.'/includes/stripe/stripe-php/init.php';
49 require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php';
50 
51 
52 if (empty($conf->stripe->enabled)) {
53  accessforbidden('', 0, 0, 1);
54 }
55 
56 
57 // You can find your endpoint's secret in your webhook settings
58 if (isset($_GET['connect'])) {
59  if (isset($_GET['test'])) {
60  $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_CONNECT_KEY;
61  $service = 'StripeTest';
62  $servicestatus = 0;
63  } else {
64  $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_CONNECT_KEY;
65  $service = 'StripeLive';
66  $servicestatus = 1;
67  }
68 } else {
69  if (isset($_GET['test'])) {
70  $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_KEY;
71  $service = 'StripeTest';
72  $servicestatus = 0;
73  } else {
74  $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_KEY;
75  $service = 'StripeLive';
76  $servicestatus = 1;
77  }
78 }
79 
80 if (empty($endpoint_secret)) {
81  print 'Error: Setup of module Stripe not complete for mode '.$service.'. The WEBHOOK_KEY is not defined.';
82  http_response_code(400); // PHP 5.4 or greater
83  exit();
84 }
85 
86 if (!empty($conf->global->STRIPE_USER_ACCOUNT_FOR_ACTIONS)) {
87  // We set the user to use for all ipn actions in Dolibarr
88  $user = new User($db);
89  $user->fetch($conf->global->STRIPE_USER_ACCOUNT_FOR_ACTIONS);
90  $user->getrights();
91 } else {
92  print 'Error: Setup of module Stripe not complete for mode '.$service.'. The STRIPE_USER_ACCOUNT_FOR_ACTIONS is not defined.';
93  http_response_code(400); // PHP 5.4 or greater
94  exit();
95 }
96 
97 
98 // TODO Add a check on a security key
99 
100 
101 
102 /*
103  * Actions
104  */
105 
106 $payload = @file_get_contents("php://input");
107 $sig_header = $_SERVER["HTTP_STRIPE_SIGNATURE"];
108 $event = null;
109 
110 $error = 0;
111 
112 try {
113  $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
114 } catch (\UnexpectedValueException $e) {
115  // Invalid payload
116  http_response_code(400); // PHP 5.4 or greater
117  exit();
118 } catch (\Stripe\Error\SignatureVerification $e) {
119  // Invalid signature
120  http_response_code(400); // PHP 5.4 or greater
121  exit();
122 }
123 
124 // Do something with $event
125 
126 $langs->load("main");
127 
128 
129 if (!empty($conf->multicompany->enabled) && !empty($conf->stripeconnect->enabled) && is_object($mc)) {
130  $sql = "SELECT entity";
131  $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
132  $sql .= " WHERE service = '".$db->escape($service)."' and tokenstring LIKE '%".$db->escape($event->account)."%'";
133 
134  dol_syslog(get_class($db)."::fetch", LOG_DEBUG);
135  $result = $db->query($sql);
136  if ($result) {
137  if ($db->num_rows($result)) {
138  $obj = $db->fetch_object($result);
139  $key = $obj->entity;
140  } else {
141  $key = 1;
142  }
143  } else {
144  $key = 1;
145  }
146  $ret = $mc->switchEntity($key);
147 }
148 
149 // list of action
150 $stripe = new Stripe($db);
151 
152 // Subject
153 $societeName = $conf->global->MAIN_INFO_SOCIETE_NOM;
154 if (!empty($conf->global->MAIN_APPLICATION_TITLE)) {
155  $societeName = $conf->global->MAIN_APPLICATION_TITLE;
156 }
157 
158 
159 dol_syslog("***** Stripe IPN was called with event->type = ".$event->type);
160 
161 
162 if ($event->type == 'payout.created') {
163  $error = 0;
164 
165  $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", date('Y-m-d H:i:s', $event->data->object->arrival_date), 'chaine', 0, '', $conf->entity);
166 
167  if ($result > 0) {
168  $subject = $societeName.' - [NOTIFICATION] Stripe payout scheduled';
169  if (!empty($user->email)) {
170  $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">";
171  } else {
172  $sendto = $conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>';
173  }
174  $replyto = $sendto;
175  $sendtocc = '';
176  if (!empty($conf->global->ONLINE_PAYMENT_SENDEMAIL)) {
177  $sendtocc = $conf->global->ONLINE_PAYMENT_SENDEMAIL.'" <'.$conf->global->ONLINE_PAYMENT_SENDEMAIL.'>';
178  }
179 
180  $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');
181 
182  $mailfile = new CMailFile(
183  $subject,
184  $sendto,
185  $replyto,
186  $message,
187  array(),
188  array(),
189  array(),
190  $sendtocc,
191  '',
192  0,
193  -1
194  );
195 
196  $ret = $mailfile->sendfile();
197 
198  http_response_code(200); // PHP 5.4 or greater
199  return 1;
200  } else {
201  $error++;
202  http_response_code(500); // PHP 5.4 or greater
203  return -1;
204  }
205 } elseif ($event->type == 'payout.paid') {
206  global $conf;
207  $error = 0;
208  $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", null, 'chaine', 0, '', $conf->entity);
209  if ($result) {
210  $langs->load("errors");
211 
212  $dateo = dol_now();
213  $label = $event->data->object->description;
214  $amount = $event->data->object->amount / 100;
215  $amount_to = $event->data->object->amount / 100;
216  require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
217 
218  $accountfrom = new Account($db);
219  $accountfrom->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS);
220 
221  $accountto = new Account($db);
222  $accountto->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS);
223 
224  if (($accountto->id != $accountfrom->id) && empty($error)) {
225  $bank_line_id_from = 0;
226  $bank_line_id_to = 0;
227  $result = 0;
228 
229  // By default, electronic transfert from bank to bank
230  $typefrom = 'PRE';
231  $typeto = 'VIR';
232 
233  if (!$error) {
234  $bank_line_id_from = $accountfrom->addline($dateo, $typefrom, $label, -1 * price2num($amount), '', '', $user);
235  }
236  if (!($bank_line_id_from > 0)) {
237  $error++;
238  }
239  if (!$error) {
240  $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount), '', '', $user);
241  }
242  if (!($bank_line_id_to > 0)) {
243  $error++;
244  }
245 
246  if (!$error) {
247  $result = $accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, DOL_URL_ROOT.'/compta/bank/line.php?rowid=', '(banktransfert)', 'banktransfert');
248  }
249  if (!($result > 0)) {
250  $error++;
251  }
252  if (!$error) {
253  $result = $accountto->add_url_line($bank_line_id_to, $bank_line_id_from, DOL_URL_ROOT.'/compta/bank/line.php?rowid=', '(banktransfert)', 'banktransfert');
254  }
255  if (!($result > 0)) {
256  $error++;
257  }
258  }
259 
260  $subject = $societeName.' - [NOTIFICATION] Stripe payout done';
261  if (!empty($user->email)) {
262  $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">";
263  } else {
264  $sendto = $conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>';
265  }
266  $replyto = $sendto;
267  $sendtocc = '';
268  if (!empty($conf->global->ONLINE_PAYMENT_SENDEMAIL)) {
269  $sendtocc = $conf->global->ONLINE_PAYMENT_SENDEMAIL.'" <'.$conf->global->ONLINE_PAYMENT_SENDEMAIL.'>';
270  }
271 
272  $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');
273 
274  $mailfile = new CMailFile(
275  $subject,
276  $sendto,
277  $replyto,
278  $message,
279  array(),
280  array(),
281  array(),
282  $sendtocc,
283  '',
284  0,
285  -1
286  );
287 
288  $ret = $mailfile->sendfile();
289 
290  http_response_code(200);
291  return 1;
292  } else {
293  $error++;
294  http_response_code(500);
295  return -1;
296  }
297 } elseif ($event->type == 'customer.source.created') {
298  //TODO: save customer's source
299 } elseif ($event->type == 'customer.source.updated') {
300  //TODO: update customer's source
301 } elseif ($event->type == 'customer.source.delete') {
302  //TODO: delete customer's source
303 } elseif ($event->type == 'customer.deleted') {
304  $db->begin();
305  $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_account WHERE key_account = '".$db->escape($event->data->object->id)."' and site='stripe'";
306  $db->query($sql);
307  $db->commit();
308 } elseif ($event->type == 'payment_intent.succeeded') { // Called when making payment with PaymentIntent method ($conf->global->STRIPE_USE_NEW_CHECKOUT is on).
309  // TODO: create fees
310  // TODO: Redirect to paymentok.php
311 } elseif ($event->type == 'payment_intent.payment_failed') {
312  // TODO: Redirect to paymentko.php
313 } elseif ($event->type == 'checkout.session.completed') { // Called when making payment with new Checkout method ($conf->global->STRIPE_USE_NEW_CHECKOUT is on).
314  // TODO: create fees
315  // TODO: Redirect to paymentok.php
316 } elseif ($event->type == 'payment_method.attached') {
317  require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
318  require_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
319  $societeaccount = new SocieteAccount($db);
320 
321  $companypaymentmode = new CompanyPaymentMode($db);
322 
323  $idthirdparty = $societeaccount->getThirdPartyID($db->escape($event->data->object->customer), 'stripe', $servicestatus);
324  if ($idthirdparty > 0) { // If the payment mode is on an external customer that is known in societeaccount, we can create the payment mode
325  $companypaymentmode->stripe_card_ref = $db->escape($event->data->object->id);
326  $companypaymentmode->fk_soc = $idthirdparty;
327  $companypaymentmode->bank = null;
328  $companypaymentmode->label = null;
329  $companypaymentmode->number = $db->escape($event->data->object->id);
330  $companypaymentmode->last_four = $db->escape($event->data->object->card->last4);
331  $companypaymentmode->card_type = $db->escape($event->data->object->card->branding);
332  $companypaymentmode->proprio = $db->escape($event->data->object->billing_details->name);
333  $companypaymentmode->exp_date_month = $db->escape($event->data->object->card->exp_month);
334  $companypaymentmode->exp_date_year = $db->escape($event->data->object->card->exp_year);
335  $companypaymentmode->cvn = null;
336  $companypaymentmode->datec = $db->escape($event->data->object->created);
337  $companypaymentmode->default_rib = 0;
338  $companypaymentmode->type = $db->escape($event->data->object->type);
339  $companypaymentmode->country_code = $db->escape($event->data->object->card->country);
340  $companypaymentmode->status = $servicestatus;
341 
342  $db->begin();
343  if (!$error) {
344  $result = $companypaymentmode->create($user);
345  if ($result < 0) {
346  $error++;
347  }
348  }
349  if (!$error) {
350  $db->commit();
351  } else {
352  $db->rollback();
353  }
354  }
355 } elseif ($event->type == 'payment_method.updated') {
356  require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php';
357  $companypaymentmode = new CompanyPaymentMode($db);
358  $companypaymentmode->fetch(0, '', 0, '', " AND stripe_card_ref = '".$db->escape($event->data->object->id)."'");
359  $companypaymentmode->bank = null;
360  $companypaymentmode->label = null;
361  $companypaymentmode->number = $db->escape($event->data->object->id);
362  $companypaymentmode->last_four = $db->escape($event->data->object->card->last4);
363  $companypaymentmode->proprio = $db->escape($event->data->object->billing_details->name);
364  $companypaymentmode->exp_date_month = $db->escape($event->data->object->card->exp_month);
365  $companypaymentmode->exp_date_year = $db->escape($event->data->object->card->exp_year);
366  $companypaymentmode->cvn = null;
367  $companypaymentmode->datec = $db->escape($event->data->object->created);
368  $companypaymentmode->default_rib = 0;
369  $companypaymentmode->type = $db->escape($event->data->object->type);
370  $companypaymentmode->country_code = $db->escape($event->data->object->card->country);
371  $companypaymentmode->status = $servicestatus;
372 
373  $db->begin();
374  if (!$error) {
375  $result = $companypaymentmode->update($user);
376  if ($result < 0) {
377  $error++;
378  }
379  }
380  if (!$error) {
381  $db->commit();
382  } else {
383  $db->rollback();
384  }
385 } elseif ($event->type == 'payment_method.detached') {
386  $db->begin();
387  $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_rib WHERE number = '".$db->escape($event->data->object->id)."' and status = ".((int) $servicestatus);
388  $db->query($sql);
389  $db->commit();
390 } elseif ($event->type == 'charge.succeeded') {
391  // TODO: create fees
392  // TODO: Redirect to paymentok.php
393 } elseif ($event->type == 'charge.failed') {
394  // TODO: Redirect to paymentko.php
395 } elseif (($event->type == 'source.chargeable') && ($event->data->object->type == 'three_d_secure') && ($event->data->object->three_d_secure->authenticated == true)) {
396  // This event is deprecated.
397 }
398 
399 http_response_code(200);
SocieteAccount
Class for SocieteAccount.
Definition: societeaccount.class.php:35
CMailFile
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Definition: CMailFile.class.php:38
price2num
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
Definition: functions.lib.php:5661
dol_print_date
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
Definition: functions.lib.php:2514
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1603
dolGetFirstLastname
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
Definition: functions.lib.php:8062
dolibarr_set_const
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).
Definition: admin.lib.php:627
User
Class to manage Dolibarr users.
Definition: user.class.php:44
dol_now
dol_now($mode='auto')
Return date for now.
Definition: functions.lib.php:2845
Stripe
Stripe class.
Definition: stripe.class.php:29
CompanyPaymentMode
Class for CompanyPaymentMode.
Definition: companypaymentmode.class.php:33
accessforbidden
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program Calling this function terminate execution ...
Definition: security.lib.php:933
Account
Class to manage bank accounts.
Definition: account.class.php:38