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