dolibarr  20.0.0-alpha
stripe.class.php
1 <?php
2 /* Copyright (C) 2018-2021 Thibault FOUCART <support@ptibogxiv.net>
3  * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
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 
20 // Put here all includes required by your class file
21 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
22 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
23 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
24 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
25 require_once DOL_DOCUMENT_ROOT.'/stripe/config.php'; // This set stripe global env
26 
27 
31 class Stripe extends CommonObject
32 {
36  public $rowid;
37 
41  public $fk_soc;
42 
46  public $fk_key;
47 
51  public $id;
52 
56  public $mode;
57 
61  public $entity;
62 
66  public $statut;
67 
68  public $type;
69 
73  public $code;
74 
78  public $declinecode;
79 
83  public $message;
84 
90  public function __construct($db)
91  {
92  $this->db = $db;
93  }
94 
95 
104  public function getStripeAccount($mode = 'StripeTest', $fk_soc = 0, $entity = -1)
105  {
106  global $conf;
107 
108  $key = '';
109  if ($entity < 0) {
110  $entity = $conf->entity;
111  }
112 
113  $sql = "SELECT tokenstring";
114  $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
115  $sql .= " WHERE service = '".$this->db->escape($mode)."'";
116  $sql .= " AND entity = ".((int) $entity);
117  if ($fk_soc > 0) {
118  $sql .= " AND fk_soc = ".((int) $fk_soc);
119  } else {
120  $sql .= " AND fk_soc IS NULL";
121  }
122  $sql .= " AND fk_user IS NULL AND fk_adherent IS NULL";
123 
124  dol_syslog(get_class($this)."::getStripeAccount", LOG_DEBUG);
125 
126  $result = $this->db->query($sql);
127  if ($result) {
128  if ($this->db->num_rows($result)) {
129  $obj = $this->db->fetch_object($result);
130  $tokenstring = $obj->tokenstring;
131 
132  if ($tokenstring) {
133  $tmparray = json_decode($tokenstring);
134  $key = empty($tmparray->stripe_user_id) ? '' : $tmparray->stripe_user_id;
135  }
136  } else {
137  $tokenstring = '';
138  }
139  } else {
140  dol_print_error($this->db);
141  }
142 
143  dol_syslog("No dedicated Stripe Connect account available for entity ".$conf->entity);
144 
145  return $key;
146  }
147 
156  public function getStripeCustomerAccount($id, $status = 0, $site_account = '')
157  {
158  include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
159  $societeaccount = new SocieteAccount($this->db);
160  return $societeaccount->getCustomerAccount($id, 'stripe', $status, $site_account); // Get thirdparty cus_...
161  }
162 
163 
174  public function customerStripe(CommonObject $object, $key = '', $status = 0, $createifnotlinkedtostripe = 0)
175  {
176  global $conf, $user;
177 
178  if (empty($object->id)) {
179  dol_syslog("customerStripe is called with the parameter object that is not loaded");
180  return null;
181  }
182 
183  $customer = null;
184 
185  // Force to use the correct API key
186  global $stripearrayofkeysbyenv;
187  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
188 
189  $sql = "SELECT sa.key_account as key_account, sa.entity"; // key_account is cus_....
190  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
191  $sql .= " WHERE sa.fk_soc = ".((int) $object->id);
192  $sql .= " AND sa.entity IN (".getEntity('societe').")";
193  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
194  $sql .= " AND (sa.site_account IS NULL OR sa.site_account = '' OR sa.site_account = '".$this->db->escape($stripearrayofkeysbyenv[$status]['publishable_key'])."')";
195  $sql .= " AND sa.key_account IS NOT NULL AND sa.key_account <> ''";
196 
197  dol_syslog(get_class($this)."::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG);
198  $resql = $this->db->query($sql);
199  if ($resql) {
200  $num = $this->db->num_rows($resql);
201  if ($num) {
202  $obj = $this->db->fetch_object($resql);
203  $tiers = $obj->key_account;
204 
205  dol_syslog(get_class($this)."::customerStripe found stripe customer key_account = ".$tiers.". We will try to read it on Stripe with publishable_key = ".$stripearrayofkeysbyenv[$status]['publishable_key']);
206 
207  try {
208  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
209  //$customer = \Stripe\Customer::retrieve("$tiers");
210  $customer = \Stripe\Customer::retrieve(array('id' => "$tiers", 'expand[]' => 'sources'));
211  } else {
212  //$customer = \Stripe\Customer::retrieve("$tiers", array("stripe_account" => $key));
213  $customer = \Stripe\Customer::retrieve(array('id' => "$tiers", 'expand[]' => 'sources'), array("stripe_account" => $key));
214  }
215  } catch (Exception $e) {
216  // For example, we may have error: 'No such customer: cus_XXXXX; a similar object exists in live mode, but a test mode key was used to make this request.'
217  $this->error = $e->getMessage();
218  }
219  } elseif ($createifnotlinkedtostripe) {
220  $ipaddress = getUserRemoteIP();
221 
222  $dataforcustomer = array(
223  "email" => $object->email,
224  "description" => $object->name,
225  "metadata" => array('dol_id' => $object->id, 'dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress)
226  );
227 
228  $vatcleaned = $object->tva_intra ? $object->tva_intra : null;
229 
230  /*
231  $taxinfo = array('type'=>'vat');
232  if ($vatcleaned)
233  {
234  $taxinfo["tax_id"] = $vatcleaned;
235  }
236  // We force data to "null" if not defined as expected by Stripe
237  if (empty($vatcleaned)) $taxinfo=null;
238  $dataforcustomer["tax_info"] = $taxinfo;
239  */
240 
241  //$a = \Stripe\Stripe::getApiKey();
242  //var_dump($a);var_dump($key);exit;
243  try {
244  // Force to use the correct API key
245  global $stripearrayofkeysbyenv;
246  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
247 
248  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
249  $customer = \Stripe\Customer::create($dataforcustomer);
250  } else {
251  $customer = \Stripe\Customer::create($dataforcustomer, array("stripe_account" => $key));
252  }
253 
254  // Create the VAT record in Stripe
255  if (getDolGlobalString('STRIPE_SAVE_TAX_IDS')) { // We setup to save Tax info on Stripe side. Warning: This may result in error when saving customer
256  if (!empty($vatcleaned)) {
257  $isineec = isInEEC($object);
258  if ($object->country_code && $isineec) {
259  //$taxids = $customer->allTaxIds($customer->id);
260  $customer->createTaxId($customer->id, array('type' => 'eu_vat', 'value' => $vatcleaned));
261  }
262  }
263  }
264 
265  // Create customer in Dolibarr
266  $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_account (fk_soc, login, key_account, site, site_account, status, entity, date_creation, fk_user_creat)";
267  $sql .= " VALUES (".((int) $object->id).", '', '".$this->db->escape($customer->id)."', 'stripe', '".$this->db->escape($stripearrayofkeysbyenv[$status]['publishable_key'])."', ".((int) $status).", ".((int) $conf->entity).", '".$this->db->idate(dol_now())."', ".((int) $user->id).")";
268  $resql = $this->db->query($sql);
269  if (!$resql) {
270  $this->error = $this->db->lasterror();
271  }
272  } catch (Exception $e) {
273  $this->error = $e->getMessage();
274  }
275  }
276  } else {
277  dol_print_error($this->db);
278  }
279 
280  return $customer;
281  }
282 
291  public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0)
292  {
293  $stripepaymentmethod = null;
294 
295  try {
296  // Force to use the correct API key
297  global $stripearrayofkeysbyenv;
298  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
299  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
300  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id);
301  } else {
302  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id, array("stripe_account" => $key));
303  }
304  } catch (Exception $e) {
305  $this->error = $e->getMessage();
306  }
307 
308  return $stripepaymentmethod;
309  }
310 
319  public function getSelectedReader($reader, $key = '', $status = 0)
320  {
321  $selectedreader = null;
322 
323  try {
324  // Force to use the correct API key
325  global $stripearrayofkeysbyenv;
326  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
327  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
328  $selectedreader = \Stripe\Terminal\Reader::retrieve((string) $reader);
329  } else {
330  $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve((string) $reader, array("stripe_account" => $key));
331  }
332  } catch (Exception $e) {
333  $this->error = $e->getMessage();
334  }
335 
336  return $selectedreader;
337  }
338 
365  public function getPaymentIntent($amount, $currency_code, $tag, $description = '', $object = null, $customer = null, $key = null, $status = 0, $usethirdpartyemailforreceiptemail = 0, $mode = 'automatic', $confirmnow = false, $payment_method = null, $off_session = 0, $noidempotency_key = 1, $did = 0)
366  {
367  global $conf, $user;
368 
369  dol_syslog(get_class($this)."::getPaymentIntent", LOG_INFO, 1);
370 
371  $error = 0;
372 
373  if (empty($status)) {
374  $service = 'StripeTest';
375  } else {
376  $service = 'StripeLive';
377  }
378 
379  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
380  if (!in_array($currency_code, $arrayzerounitcurrency)) {
381  $stripeamount = $amount * 100;
382  } else {
383  $stripeamount = $amount;
384  }
385 
386  $fee = 0;
387  if (getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT")) {
388  $fee = $amount * ((float) getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT", '0') / 100) + (float) getDolGlobalString("STRIPE_APPLICATION_FEE", '0');
389  }
390  if ($fee >= (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') && (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') > (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
391  $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0');
392  } elseif ($fee < (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
393  $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0');
394  }
395  if (!in_array($currency_code, $arrayzerounitcurrency)) {
396  $stripefee = round($fee * 100);
397  } else {
398  $stripefee = round($fee);
399  }
400 
401  $paymentintent = null;
402 
403  if (is_object($object) && getDolGlobalInt('STRIPE_REUSE_EXISTING_INTENT_IF_FOUND') && !getDolGlobalInt('STRIPE_CARD_PRESENT')) {
404  // Warning. If a payment was tried and failed, a payment intent was created.
405  // But if we change something on object to pay (amount or other that does not change the idempotency key), reusing same payment intent is not allowed by Stripe.
406  // Recommended solution is to recreate a new payment intent each time we need one (old one will be automatically closed by Stripe after a delay), Stripe will
407  // automatically return the existing payment intent if idempotency is provided when we try to create the new one.
408  // That's why we can comment the part of code to retrieve a payment intent with object id (never mind if we cumulate payment intent with old ones that will not be used)
409 
410  $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
411  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
412  $sql .= " WHERE pi.fk_facture = ".((int) $object->id);
413  $sql .= " AND pi.sourcetype = '".$this->db->escape($object->element)."'";
414  $sql .= " AND pi.entity IN (".getEntity('societe').")";
415  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
416 
417  dol_syslog(get_class($this)."::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
418  $resql = $this->db->query($sql);
419  if ($resql) {
420  $num = $this->db->num_rows($resql);
421  if ($num) {
422  $obj = $this->db->fetch_object($resql);
423  $intent = $obj->ext_payment_id;
424 
425  dol_syslog(get_class($this)."::getPaymentIntent found existing payment intent record");
426 
427  // Force to use the correct API key
428  global $stripearrayofkeysbyenv;
429  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
430 
431  try {
432  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
433  $paymentintent = \Stripe\PaymentIntent::retrieve($intent);
434  } else {
435  $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
436  }
437  } catch (Exception $e) {
438  $error++;
439  $this->error = $e->getMessage();
440  }
441  }
442  }
443  }
444 
445  if (empty($paymentintent)) {
446  // Try to create intent. See https://stripe.com/docs/api/payment_intents/create
447  $ipaddress = getUserRemoteIP();
448  $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
449  if (is_object($object)) {
450  $metadata['dol_type'] = $object->element;
451  $metadata['dol_id'] = $object->id;
452  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
453  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
454  }
455  }
456 
457  // list of payment method types
458  $paymentmethodtypes = array("card");
459  $descriptor = dol_trunc($tag, 10, 'right', 'UTF-8', 1);
460  if (getDolGlobalInt('STRIPE_SEPA_DIRECT_DEBIT')) {
461  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
462  //$descriptor = preg_replace('/ref=[^:=]+/', '', $descriptor); // Clean ref
463  }
464  if (getDolGlobalInt('STRIPE_KLARNA')) {
465  $paymentmethodtypes[] = "klarna";
466  }
467  if (getDolGlobalInt('STRIPE_BANCONTACT')) {
468  $paymentmethodtypes[] = "bancontact";
469  }
470  if (getDolGlobalInt('STRIPE_IDEAL')) {
471  $paymentmethodtypes[] = "ideal";
472  }
473  if (getDolGlobalInt('STRIPE_GIROPAY')) {
474  $paymentmethodtypes[] = "giropay";
475  }
476  if (getDolGlobalInt('STRIPE_SOFORT')) {
477  $paymentmethodtypes[] = "sofort";
478  }
479  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
480  $paymentmethodtypes = array("card_present");
481  }
482 
483  global $dolibarr_main_url_root;
484 
485  $dataforintent = array(
486  "confirm" => $confirmnow, // try to confirm immediately after create (if conditions are ok)
487  "confirmation_method" => $mode,
488  "amount" => $stripeamount,
489  "currency" => $currency_code,
490  "payment_method_types" => $paymentmethodtypes, // When payment_method_types is set, return_url is not required but payment mode can't be managed from dashboard
491  /*
492  'return_url' => $dolibarr_main_url_root.'/public/payment/paymentok.php',
493  'automatic_payment_methods' => array(
494  'enabled' => true,
495  'allow_redirects' => 'never',
496  ),
497  */
498  "description" => $description,
499  //"save_payment_method" => true,
500  "setup_future_usage" => "on_session",
501  "metadata" => $metadata
502  );
503  if ($descriptor) {
504  $dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
505  $dataforintent["statement_descriptor"] = $descriptor; // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
506  }
507  if (!is_null($customer)) {
508  $dataforintent["customer"] = $customer;
509  }
510  // payment_method =
511  // payment_method_types = array('card')
512  //var_dump($dataforintent);
513  if ($off_session) {
514  unset($dataforintent['setup_future_usage']);
515  // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
516  // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
517  //$dataforintent["setup_future_usage"] = "off_session";
518  $dataforintent["off_session"] = true;
519  }
520  if (getDolGlobalInt('STRIPE_GIROPAY')) {
521  unset($dataforintent['setup_future_usage']);
522  }
523  if (getDolGlobalInt('STRIPE_KLARNA')) {
524  unset($dataforintent['setup_future_usage']);
525  }
526  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
527  unset($dataforintent['setup_future_usage']);
528  $dataforintent["capture_method"] = "manual";
529  $dataforintent["confirmation_method"] = "manual";
530  }
531  if (!is_null($payment_method)) {
532  $dataforintent["payment_method"] = $payment_method;
533  $description .= ' - '.$payment_method;
534  }
535 
536  if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
537  $dataforintent["application_fee_amount"] = $stripefee;
538  }
539  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
540  $dataforintent["receipt_email"] = $object->thirdparty->email;
541  }
542 
543  try {
544  // Force to use the correct API key
545  global $stripearrayofkeysbyenv;
546  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
547 
548  $arrayofoptions = array();
549  if (empty($noidempotency_key)) {
550  $arrayofoptions["idempotency_key"] = $description;
551  }
552  // Note: If all data for payment intent are same than a previous on, even if we use 'create', Stripe will return ID of the old existing payment intent.
553  if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
554  $arrayofoptions["stripe_account"] = $key;
555  }
556 
557  dol_syslog("dataforintent to create paymentintent = ".var_export($dataforintent, true));
558 
559  $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
560 
561  // Store the payment intent
562  if (is_object($object)) {
563  $paymentintentalreadyexists = 0;
564 
565  if ($did > 0) {
566  // If a payment request line provided, we do not need to recreate one, we just update it
567  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
568 
569  $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
570  $sql .= " ext_payment_site = '".$this->db->escape($service)."',";
571  $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
572  $sql .= " WHERE rowid = ".((int) $did);
573 
574  $resql = $this->db->query($sql);
575  if ($resql) {
576  $paymentintentalreadyexists++;
577  } else {
578  $error++;
579  dol_print_error($this->db);
580  }
581  } else {
582  // Check that payment intent $paymentintent->id is not already recorded.
583  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
584 
585  $sql = "SELECT pi.rowid";
586  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
587  $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
588  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
589  $sql .= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
590 
591  $resql = $this->db->query($sql);
592  if ($resql) {
593  $num = $this->db->num_rows($resql);
594  if ($num) {
595  $obj = $this->db->fetch_object($resql);
596  if ($obj) {
597  $paymentintentalreadyexists++;
598  }
599  }
600  } else {
601  $error++;
602  dol_print_error($this->db);
603  }
604  }
605 
606  // If not, we create it.
607  if (!$error && !$paymentintentalreadyexists) {
608  $now = dol_now();
609  $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
610  $sql .= " VALUES ('".$this->db->idate($now)."', ".((int) $user->id).", '".$this->db->escape($paymentintent->id)."', ".((int) $object->id).", '".$this->db->escape($object->element)."', ".((int) $conf->entity).", '".$this->db->escape($service)."', ".((float) $amount).")";
611  $resql = $this->db->query($sql);
612  if (!$resql) {
613  $error++;
614  $this->error = $this->db->lasterror();
615  dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
616  }
617  }
618  } else {
619  $_SESSION["stripe_payment_intent"] = $paymentintent;
620  }
621  } catch (Stripe\Exception\CardException $e) {
622  $error++;
623  $this->error = $e->getMessage();
624  $this->code = $e->getStripeCode();
625  $this->declinecode = $e->getDeclineCode();
626  } catch (Exception $e) {
627  //var_dump($dataforintent);
628  //var_dump($description);
629  //var_dump($key);
630  //var_dump($paymentintent);
631  //var_dump($e->getMessage());
632  //var_dump($e);
633  $error++;
634  $this->error = $e->getMessage();
635  $this->code = '';
636  $this->declinecode = '';
637  }
638  }
639 
640  dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
641 
642  if (!$error) {
643  return $paymentintent;
644  } else {
645  return null;
646  }
647  }
648 
667  public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
668  {
669  global $conf;
670 
671  dol_syslog("getSetupIntent description=".$description.' confirmnow='.json_encode($confirmnow), LOG_INFO, 1);
672 
673  $error = 0;
674 
675  if (empty($status)) {
676  $service = 'StripeTest';
677  } else {
678  $service = 'StripeLive';
679  }
680 
681  $setupintent = null;
682 
683  if (empty($setupintent)) {
684  $ipaddress = getUserRemoteIP();
685  $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
686  if (is_object($object)) {
687  $metadata['dol_type'] = $object->element;
688  $metadata['dol_id'] = $object->id;
689  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
690  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
691  }
692  }
693 
694  // list of payment method types
695  $paymentmethodtypes = array("card");
696  if (getDolGlobalString('STRIPE_SEPA_DIRECT_DEBIT')) {
697  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
698  }
699  if (getDolGlobalString('STRIPE_BANCONTACT')) {
700  $paymentmethodtypes[] = "bancontact";
701  }
702  if (getDolGlobalString('STRIPE_IDEAL')) {
703  $paymentmethodtypes[] = "ideal";
704  }
705  // Giropay not possible for setup intent
706  if (getDolGlobalString('STRIPE_SOFORT')) {
707  $paymentmethodtypes[] = "sofort";
708  }
709 
710  global $dolibarr_main_url_root;
711 
712  $dataforintent = array(
713  "confirm" => $confirmnow, // Do not confirm immediately during creation of intent
714  "payment_method_types" => $paymentmethodtypes, // When payment_method_types is set, return_url is not required but payment mode can't be managed from dashboard
715  /*
716  'return_url' => $dolibarr_main_url_root.'/public/payment/paymentok.php',
717  'automatic_payment_methods' => array(
718  'enabled' => true,
719  'allow_redirects' => 'never',
720  ),
721  */
722  "usage" => "off_session",
723  "metadata" => $metadata
724  );
725  if (!is_null($customer)) {
726  $dataforintent["customer"] = $customer;
727  }
728  if (!is_null($description)) {
729  $dataforintent["description"] = $description;
730  }
731  // payment_method =
732  // payment_method_types = array('card')
733  //var_dump($dataforintent);
734 
735  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
736  $dataforintent["receipt_email"] = $object->thirdparty->email;
737  }
738 
739  try {
740  // Force to use the correct API key
741  global $stripearrayofkeysbyenv;
742  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
743 
744  dol_syslog("getSetupIntent ".$stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG);
745 
746  // Note: If all data for payment intent are same than a previous one, even if we use 'create', Stripe will return ID of the old existing payment intent.
747  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
748  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
749  $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
750  } else {
751  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
752  $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
753  }
754  //var_dump($setupintent->id);
755 
756  // Store the setup intent
757  /*if (is_object($object))
758  {
759  $setupintentalreadyexists = 0;
760  // Check that payment intent $setupintent->id is not already recorded.
761  $sql = "SELECT pi.rowid";
762  $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
763  $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
764  $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
765  $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
766 
767  dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
768  $resql = $this->db->query($sql);
769  if ($resql) {
770  $num = $this->db->num_rows($resql);
771  if ($num)
772  {
773  $obj = $this->db->fetch_object($resql);
774  if ($obj) $setupintentalreadyexists++;
775  }
776  }
777  else dol_print_error($this->db);
778 
779  // If not, we create it.
780  if (! $setupintentalreadyexists)
781  {
782  $now=dol_now();
783  $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
784  $sql .= " VALUES ('".$this->db->idate($now)."', ".((int) $user->id).", '".$this->db->escape($setupintent->id)."', ".((int) $object->id).", '".$this->db->escape($object->element)."', " . ((int) $conf->entity) . ", '" . $this->db->escape($service) . "', ".((float) $amount).")";
785  $resql = $this->db->query($sql);
786  if (! $resql)
787  {
788  $error++;
789  $this->error = $this->db->lasterror();
790  dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
791  }
792  }
793  }
794  else
795  {
796  $_SESSION["stripe_setup_intent"] = $setupintent;
797  }*/
798  } catch (Exception $e) {
799  //var_dump($dataforintent);
800  //var_dump($description);
801  //var_dump($key);
802  //var_dump($setupintent);
803  //var_dump($e->getMessage());
804  $error++;
805  $this->error = $e->getMessage();
806  }
807  }
808 
809  if (!$error) {
810  dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
811  return $setupintent;
812  } else {
813  dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
814  return null;
815  }
816  }
817 
818 
829  public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
830  {
831  global $conf, $user, $langs;
832 
833  $card = null;
834 
835  $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.exp_date_month, sa.exp_date_year, sa.number, sa.cvn"; // stripe_card_ref is card_....
836  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
837  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
838  $sql .= " AND sa.type = 'card'";
839 
840  dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
841  $resql = $this->db->query($sql);
842  if ($resql) {
843  $num = $this->db->num_rows($resql);
844  if ($num) {
845  $obj = $this->db->fetch_object($resql);
846  $cardref = $obj->stripe_card_ref;
847  dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
848  if ($cardref) {
849  try {
850  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
851  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
852  $card = $cu->sources->retrieve($cardref);
853  } else {
854  $card = \Stripe\PaymentMethod::retrieve($cardref);
855  }
856  } else {
857  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
858  //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
859  $card = $cu->sources->retrieve($cardref);
860  } else {
861  //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
862  $card = \Stripe\PaymentMethod::retrieve($cardref);
863  }
864  }
865  } catch (Exception $e) {
866  $this->error = $e->getMessage();
867  dol_syslog($this->error, LOG_WARNING);
868  }
869  } elseif ($createifnotlinkedtostripe) {
870  // Deprecated with new Stripe API and SCA. We should not use anymore this part of code now.
871  $exp_date_month = $obj->exp_date_month;
872  $exp_date_year = $obj->exp_date_year;
873  $number = $obj->number;
874  $cvc = $obj->cvn; // cvn in database, cvc for stripe
875  $cardholdername = $obj->proprio;
876 
877  $ipaddress = getUserRemoteIP();
878 
879  $dataforcard = array(
880  "source" => array(
881  'object' => 'card',
882  'exp_month' => $exp_date_month,
883  'exp_year' => $exp_date_year,
884  'number' => $number,
885  'cvc' => $cvc,
886  'name' => $cardholdername
887  ),
888  "metadata" => array(
889  'dol_type' => $object->element,
890  'dol_id' => $object->id,
891  'dol_version' => DOL_VERSION,
892  'dol_entity' => $conf->entity,
893  'ipaddress' => $ipaddress
894  )
895  );
896 
897  //$a = \Stripe\Stripe::getApiKey();
898  //var_dump($a);
899  //var_dump($stripeacc);exit;
900  try {
901  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
902  if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) {
903  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
904  $card = $cu->sources->create($dataforcard);
905  if (!$card) {
906  $this->error = 'Creation of card on Stripe has failed';
907  }
908  } else {
909  $connect = '';
910  if (!empty($stripeacc)) {
911  $connect = $stripeacc.'/';
912  }
913  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
914  if ($status) {
915  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
916  }
917  $urtoswitchonstripe = '<a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
918 
919  //dol_syslog("Error: This case is not supported", LOG_ERR);
920  $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}'));
921  }
922  } else {
923  if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) {
924  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
925  $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
926  if (!$card) {
927  $this->error = 'Creation of card on Stripe has failed';
928  }
929  } else {
930  $connect = '';
931  if (!empty($stripeacc)) {
932  $connect = $stripeacc.'/';
933  }
934  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
935  if ($status) {
936  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
937  }
938  $urtoswitchonstripe = '<a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
939 
940  //dol_syslog("Error: This case is not supported", LOG_ERR);
941  $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}'));
942  }
943  }
944 
945  if ($card) {
946  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
947  $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
948  $sql .= " country_code = '".$this->db->escape($card->country)."',";
949  $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
950  $sql .= " WHERE rowid = ".((int) $object->id);
951  $sql .= " AND type = 'card'";
952  $resql = $this->db->query($sql);
953  if (!$resql) {
954  $this->error = $this->db->lasterror();
955  }
956  }
957  } catch (Exception $e) {
958  $this->error = $e->getMessage();
959  dol_syslog($this->error, LOG_WARNING);
960  }
961  }
962  }
963  } else {
964  dol_print_error($this->db);
965  }
966 
967  return $card;
968  }
969 
970 
981  public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
982  {
983  global $conf;
984  $sepa = null;
985 
986  $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
987  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
988  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
989  $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
990 
991  $soc = new Societe($this->db);
992  $soc->fetch($object->fk_soc);
993 
994  dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
995  $resql = $this->db->query($sql);
996  if ($resql) {
997  $num = $this->db->num_rows($resql);
998  if ($num) {
999  $obj = $this->db->fetch_object($resql);
1000  $cardref = $obj->stripe_card_ref;
1001  dol_syslog(get_class($this)."::sepaStripe paymentmode=".$cardref);
1002  if ($cardref) {
1003  try {
1004  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
1005  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
1006  $sepa = $cu->sources->retrieve($cardref);
1007  } else {
1008  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
1009  }
1010  } else {
1011  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
1012  //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
1013  $sepa = $cu->sources->retrieve($cardref);
1014  } else {
1015  //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
1016  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
1017  }
1018  }
1019  } catch (Exception $e) {
1020  $this->error = $e->getMessage();
1021  dol_syslog($this->error, LOG_WARNING);
1022  }
1023  } elseif ($createifnotlinkedtostripe) {
1024  $iban = $obj->iban;
1025  $ipaddress = getUserRemoteIP();
1026  $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
1027  if (is_object($object)) {
1028  $metadata['dol_type'] = $object->element;
1029  $metadata['dol_id'] = $object->id;
1030  $metadata['dol_thirdparty_id'] = $soc->id;
1031  }
1032 
1033  $description = 'SEPA for IBAN '.$iban;
1034 
1035  $dataforcard = array(
1036  'type' => 'sepa_debit',
1037  "sepa_debit" => array('iban' => $iban),
1038  'billing_details' => array(
1039  'name' => $soc->name,
1040  'email' => !empty($soc->email) ? $soc->email : "",
1041  ),
1042  "metadata" => $metadata
1043  );
1044  // Complete owner name
1045  if (!empty($soc->town)) {
1046  $dataforcard['billing_details']['address']['city'] = $soc->town;
1047  }
1048  if (!empty($soc->country_code)) {
1049  $dataforcard['billing_details']['address']['country'] = $soc->country_code;
1050  }
1051  if (!empty($soc->address)) {
1052  $dataforcard['billing_details']['address']['line1'] = $soc->address;
1053  }
1054  if (!empty($soc->zip)) {
1055  $dataforcard['billing_details']['address']['postal_code'] = $soc->zip;
1056  }
1057  if (!empty($soc->state)) {
1058  $dataforcard['billing_details']['address']['state'] = $soc->state;
1059  }
1060 
1061  //$a = \Stripe\Stripe::getApiKey();
1062  //var_dump($a);var_dump($stripeacc);exit;
1063  try {
1064  dol_syslog("Try to create sepa_debit");
1065 
1066  $service = 'StripeTest';
1067  $servicestatus = 0;
1068  if (getDolGlobalString('STRIPE_LIVE') && !GETPOST('forcesandbox', 'alpha')) {
1069  $service = 'StripeLive';
1070  $servicestatus = 1;
1071  }
1072  // Force to use the correct API key
1073  global $stripearrayofkeysbyenv;
1074  $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
1075 
1076  dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
1077 
1078  $s = new \Stripe\StripeClient($stripeacc);
1079 
1080  //var_dump($dataforcard);exit;
1081 
1082  $sepa = $s->paymentMethods->create($dataforcard);
1083  if (!$sepa) {
1084  $this->error = 'Creation of payment method sepa_debit on Stripe has failed';
1085  } else {
1086  // link customer and src
1087  //$cs = $this->getSetupIntent($description, $soc, $cu, '', $status);
1088  $dataforintent = array(0 => ['description' => $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata' => $metadata);
1089 
1090  $cs = $s->setupIntents->create($dataforintent);
1091  //$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]);
1092  $cs = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]);
1093  // note: $cs->mandate contains ID of mandate on Stripe side
1094 
1095  if (!$cs) {
1096  $this->error = 'Link SEPA <-> Customer failed';
1097  } else {
1098  dol_syslog("Update the payment mode of the customer");
1099 
1100  // print json_encode($sepa);
1101 
1102  // Save the Stripe payment mode ID into the Dolibarr database
1103  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
1104  $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."',";
1105  $sql .= " card_type = 'sepa_debit',";
1106  $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "',";
1107  $sql .= " ext_payment_site = '".$this->db->escape($service)."'";
1108  if (!empty($cs->mandate)) {
1109  $mandateservice = new \Stripe\Mandate($stripeacc);
1110  $mandate = $mandateservice->retrieve($cs->mandate);
1111  if (is_object($mandate) && is_object($mandate->payment_method_details) && is_object($mandate->payment_method_details->sepa_debit)) {
1112  $refmandate = $mandate->payment_method_details->sepa_debit->reference;
1113  //$urlmandate = $mandate->payment_method_details->sepa_debit->url;
1114  $sql .= ", rum = '".$this->db->escape($refmandate)."'";
1115  }
1116  $sql .= ", comment = '".$this->db->escape($cs->mandate)."'";
1117  $sql .= ", date_rum = '".$this->db->idate(dol_now())."'";
1118  }
1119  $sql .= " WHERE rowid = ".((int) $object->id);
1120  $sql .= " AND type = 'ban'";
1121  $resql = $this->db->query($sql);
1122  if (!$resql) {
1123  $this->error = $this->db->lasterror();
1124  }
1125  }
1126  }
1127  } catch (Exception $e) {
1128  $sepa = null;
1129  $this->error = 'Stripe error: '.$e->getMessage().'. Check the BAN information.';
1130  dol_syslog($this->error, LOG_WARNING); // Error from Stripe, so a warning on Dolibarr
1131  }
1132  }
1133  }
1134  } else {
1135  dol_print_error($this->db);
1136  }
1137 
1138  return $sepa;
1139  }
1140 
1141 
1158  public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
1159  {
1160  global $conf;
1161 
1162  $error = 0;
1163 
1164  if (empty($status)) {
1165  $service = 'StripeTest';
1166  } else {
1167  $service = 'StripeLive';
1168  }
1169 
1170  $sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
1171  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
1172  $sql .= " WHERE sa.key_account = '".$this->db->escape($customer)."'";
1173  //$sql.= " AND sa.entity IN (".getEntity('societe').")";
1174  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
1175 
1176  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1177  $result = $this->db->query($sql);
1178  if ($result) {
1179  if ($this->db->num_rows($result)) {
1180  $obj = $this->db->fetch_object($result);
1181  $key = $obj->fk_soc;
1182  } else {
1183  $key = null;
1184  }
1185  } else {
1186  $key = null;
1187  }
1188 
1189  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
1190  if (!in_array($currency, $arrayzerounitcurrency)) {
1191  $stripeamount = $amount * 100;
1192  } else {
1193  $stripeamount = $amount;
1194  }
1195 
1196  $societe = new Societe($this->db);
1197  if ($key > 0) {
1198  $societe->fetch($key);
1199  }
1200 
1201  $description = "";
1202  $ref = "";
1203  if ($origin == 'order') {
1204  $order = new Commande($this->db);
1205  $order->fetch($item);
1206  $ref = $order->ref;
1207  $description = "ORD=".$ref.".CUS=".$societe->id.".PM=stripe";
1208  } elseif ($origin == 'invoice') {
1209  $invoice = new Facture($this->db);
1210  $invoice->fetch($item);
1211  $ref = $invoice->ref;
1212  $description = "INV=".$ref.".CUS=".$societe->id.".PM=stripe";
1213  }
1214 
1215  $ipaddress = getUserRemoteIP();
1216 
1217  $metadata = array(
1218  "dol_id" => (string) $item,
1219  "dol_type" => (string) $origin,
1220  "dol_thirdparty_id" => (string) $societe->id,
1221  'dol_thirdparty_name' => $societe->name,
1222  'dol_version' => DOL_VERSION,
1223  'dol_entity' => $conf->entity,
1224  'ipaddress' => $ipaddress
1225  );
1226  $return = new Stripe($this->db);
1227  try {
1228  // Force to use the correct API key
1229  global $stripearrayofkeysbyenv;
1230  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
1231 
1232  if (empty($conf->stripeconnect->enabled)) { // With a common Stripe account
1233  if (preg_match('/pm_/i', $source)) {
1234  $stripecard = $source;
1235  $amountstripe = $stripeamount;
1236  $FULLTAG = 'PFBO'; // Payment From Back Office
1237  $stripe = $return;
1238  $amounttopay = $amount;
1239  $servicestatus = $status;
1240 
1241  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1242  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1243 
1244  dol_syslog("* createPaymentStripe Create payment for customer ".$customer->id." on source card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1245 
1246  // Create payment intent and charge payment (confirmnow = true)
1247  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1248 
1249  $charge = new stdClass();
1250  if ($paymentintent->status == 'succeeded') {
1251  $charge->status = 'ok';
1252  } else {
1253  $charge->status = 'failed';
1254  $charge->failure_code = $stripe->code;
1255  $charge->failure_message = $stripe->error;
1256  $charge->failure_declinecode = $stripe->declinecode;
1257  $stripefailurecode = $stripe->code;
1258  $stripefailuremessage = $stripe->error;
1259  $stripefailuredeclinecode = $stripe->declinecode;
1260  }
1261  } elseif (preg_match('/acct_/i', $source)) {
1262  $charge = \Stripe\Charge::create(array(
1263  "amount" => "$stripeamount",
1264  "currency" => "$currency",
1265  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1266  "description" => "Stripe payment: ".$description,
1267  "capture" => $capture,
1268  "metadata" => $metadata,
1269  "source" => "$source"
1270  ));
1271  } else {
1272  $paymentarray = array(
1273  "amount" => "$stripeamount",
1274  "currency" => "$currency",
1275  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1276  "description" => "Stripe payment: ".$description,
1277  "capture" => $capture,
1278  "metadata" => $metadata,
1279  "source" => "$source",
1280  "customer" => "$customer"
1281  );
1282 
1283  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1284  $paymentarray["receipt_email"] = $societe->email;
1285  }
1286 
1287  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
1288  }
1289  } else {
1290  // With Stripe Connect
1291  $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
1292  if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1293  $fee = getDolGlobalString('STRIPE_APPLICATION_FEE_MAXIMAL');
1294  } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1295  $fee = getDolGlobalString('STRIPE_APPLICATION_FEE_MINIMAL');
1296  }
1297 
1298  if (!in_array($currency, $arrayzerounitcurrency)) {
1299  $stripefee = round($fee * 100);
1300  } else {
1301  $stripefee = round($fee);
1302  }
1303 
1304  $paymentarray = array(
1305  "amount" => "$stripeamount",
1306  "currency" => "$currency",
1307  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1308  "description" => "Stripe payment: ".$description,
1309  "capture" => $capture,
1310  "metadata" => $metadata,
1311  "source" => "$source",
1312  "customer" => "$customer"
1313  );
1314  if ($conf->entity != $conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0) {
1315  $paymentarray["application_fee_amount"] = $stripefee;
1316  }
1317  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1318  $paymentarray["receipt_email"] = $societe->email;
1319  }
1320 
1321  if (preg_match('/pm_/i', $source)) {
1322  $stripecard = $source;
1323  $amountstripe = $stripeamount;
1324  $FULLTAG = 'PFBO'; // Payment From Back Office
1325  $stripe = $return;
1326  $amounttopay = $amount;
1327  $servicestatus = $status;
1328 
1329  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1330  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1331 
1332  dol_syslog("* createPaymentStripe Create payment on card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1333 
1334  // Create payment intent and charge payment (confirmnow = true)
1335  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1336 
1337  $charge = new stdClass();
1338  if ($paymentintent->status == 'succeeded') {
1339  $charge->status = 'ok';
1340  $charge->id = $paymentintent->id;
1341  } else {
1342  $charge->status = 'failed';
1343  $charge->failure_code = $stripe->code;
1344  $charge->failure_message = $stripe->error;
1345  $charge->failure_declinecode = $stripe->declinecode;
1346  }
1347  } else {
1348  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
1349  }
1350  }
1351  if (isset($charge->id)) {
1352  }
1353 
1354  $return->statut = 'success';
1355  $return->id = $charge->id;
1356 
1357  if (preg_match('/pm_/i', $source)) {
1358  $return->message = 'Payment retrieved by card status = '.$charge->status;
1359  } else {
1360  if ($charge->source->type == 'card') {
1361  $return->message = $charge->source->card->brand." ....".$charge->source->card->last4;
1362  } elseif ($charge->source->type == 'three_d_secure') {
1363  $stripe = new Stripe($this->db);
1364  $src = \Stripe\Source::retrieve("".$charge->source->three_d_secure->card, array(
1365  "stripe_account" => $stripe->getStripeAccount($service)
1366  ));
1367  $return->message = $src->card->brand." ....".$src->card->last4;
1368  } else {
1369  $return->message = $charge->id;
1370  }
1371  }
1372  } catch (\Stripe\Exception\CardException $e) {
1373  include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
1374  // Since it's a decline, \Stripe\Exception\Card will be caught
1375  $body = $e->getJsonBody();
1376  $err = $body['error'];
1377 
1378  $return->statut = 'error';
1379  $return->id = $err['charge'];
1380  $return->type = $err['type'];
1381  $return->code = $err['code'];
1382  $return->message = $err['message'];
1383  $body = "Error: <br>".$return->id." ".$return->message." ";
1384  $subject = '[Alert] Payment error using Stripe';
1385  $cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
1386  $cmailfile->sendfile();
1387 
1388  $error++;
1389  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1390  } catch (\Stripe\Exception\RateLimitException $e) {
1391  // Too many requests made to the API too quickly
1392  $error++;
1393  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1394  } catch (\Stripe\Exception\InvalidRequestException $e) {
1395  // Invalid parameters were supplied to Stripe's API
1396  $error++;
1397  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1398  } catch (\Stripe\Exception\AuthenticationException $e) {
1399  // Authentication with Stripe's API failed
1400  // (maybe you changed API keys recently)
1401  $error++;
1402  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1403  } catch (\Stripe\Exception\ApiConnectionException $e) {
1404  // Network communication with Stripe failed
1405  $error++;
1406  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1407  } catch (\Stripe\Exception\ExceptionInterface $e) {
1408  // Display a very generic error to the user, and maybe send
1409  // yourself an email
1410  $error++;
1411  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1412  } catch (Exception $e) {
1413  // Something else happened, completely unrelated to Stripe
1414  $error++;
1415  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1416  }
1417  return $return;
1418  }
1419 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Class to manage customers orders.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class for CompanyPaymentMode.
Class to manage invoices.
Class for SocieteAccount.
Class to manage third parties objects (customers, suppliers, prospects...)
Stripe class.
getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail=0, $confirmnow=false)
Get the Stripe payment intent.
getPaymentMethodStripe($paymentmethod, $key='', $status=0)
Get the Stripe payment method Object from its ID.
sepaStripe($cu, CompanyPaymentMode $object, $stripeacc='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe SEPA of a company payment mode (create it if it doesn't exists and $createifnotlinkedt...
getPaymentIntent($amount, $currency_code, $tag, $description='', $object=null, $customer=null, $key=null, $status=0, $usethirdpartyemailforreceiptemail=0, $mode='automatic', $confirmnow=false, $payment_method=null, $off_session=0, $noidempotency_key=1, $did=0)
Get the Stripe payment intent.
getStripeCustomerAccount($id, $status=0, $site_account='')
getStripeCustomerAccount
customerStripe(CommonObject $object, $key='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe customer of a thirdparty (with option to create it in Stripe if not linked yet).
cardStripe($cu, CompanyPaymentMode $object, $stripeacc='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe card of a company payment mode (option to create it on Stripe if not linked yet is no ...
createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status=0, $usethirdpartyemailforreceiptemail=0, $capture=true)
Create charge.
__construct($db)
Constructor.
getStripeAccount($mode='StripeTest', $fk_soc=0, $entity=-1)
Return main company OAuth Connect stripe account.
getSelectedReader($reader, $key='', $status=0)
Get the Stripe reader Object from its ID.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
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.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
div float
Buy price without taxes.
Definition: style.css.php:959
print *****$script_file(".$version.") pid code
1: frais de port 2: ecotaxe 3: option line (when qty = 0)