dolibarr  18.0.0
stripe.class.php
1 <?php
2 /* Copyright (C) 2018-2021 Thibault FOUCART <support@ptibogxiv.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 // Put here all includes required by your class file
19 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
20 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
21 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
22 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
23 require_once DOL_DOCUMENT_ROOT.'/stripe/config.php'; // This set stripe global env
24 
25 
29 class Stripe extends CommonObject
30 {
34  public $rowid;
35 
39  public $fk_soc;
40 
44  public $fk_key;
45 
49  public $id;
50 
51  public $mode;
52 
56  public $entity;
57 
58  public $statut;
59 
60  public $type;
61 
62  public $code;
63  public $declinecode;
64 
68  public $message;
69 
75  public function __construct($db)
76  {
77  $this->db = $db;
78  }
79 
80 
89  public function getStripeAccount($mode = 'StripeTest', $fk_soc = 0, $entity = -1)
90  {
91  global $conf;
92 
93  $key = '';
94  if ($entity < 0) {
95  $entity = $conf->entity;
96  }
97 
98  $sql = "SELECT tokenstring";
99  $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
100  $sql .= " WHERE service = '".$this->db->escape($mode)."'";
101  $sql .= " AND entity = ".((int) $entity);
102  if ($fk_soc > 0) {
103  $sql .= " AND fk_soc = ".((int) $fk_soc);
104  } else {
105  $sql .= " AND fk_soc IS NULL";
106  }
107  $sql .= " AND fk_user IS NULL AND fk_adherent IS NULL";
108 
109  dol_syslog(get_class($this)."::getStripeAccount", LOG_DEBUG);
110 
111  $result = $this->db->query($sql);
112  if ($result) {
113  if ($this->db->num_rows($result)) {
114  $obj = $this->db->fetch_object($result);
115  $tokenstring = $obj->tokenstring;
116 
117  if ($tokenstring) {
118  $tmparray = json_decode($tokenstring);
119  $key = empty($tmparray->stripe_user_id) ? '' : $tmparray->stripe_user_id;
120  }
121  } else {
122  $tokenstring = '';
123  }
124  } else {
125  dol_print_error($this->db);
126  }
127 
128  dol_syslog("No dedicated Stripe Connect account available for entity ".$conf->entity);
129 
130  return $key;
131  }
132 
141  public function getStripeCustomerAccount($id, $status = 0, $site_account = '')
142  {
143  include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
144  $societeaccount = new SocieteAccount($this->db);
145  return $societeaccount->getCustomerAccount($id, 'stripe', $status, $site_account); // Get thirdparty cus_...
146  }
147 
148 
159  public function customerStripe(Societe $object, $key = '', $status = 0, $createifnotlinkedtostripe = 0)
160  {
161  global $conf, $user;
162 
163  if (empty($object->id)) {
164  dol_syslog("customerStripe is called with the parameter object that is not loaded");
165  return null;
166  }
167 
168  $customer = null;
169 
170  // Force to use the correct API key
171  global $stripearrayofkeysbyenv;
172  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
173 
174  $sql = "SELECT sa.key_account as key_account, sa.entity"; // key_account is cus_....
175  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
176  $sql .= " WHERE sa.fk_soc = ".((int) $object->id);
177  $sql .= " AND sa.entity IN (".getEntity('societe').")";
178  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
179  $sql .= " AND (sa.site_account IS NULL OR sa.site_account = '' OR sa.site_account = '".$this->db->escape($stripearrayofkeysbyenv[$status]['publishable_key'])."')";
180  $sql .= " AND sa.key_account IS NOT NULL AND sa.key_account <> ''";
181 
182  dol_syslog(get_class($this)."::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG);
183  $resql = $this->db->query($sql);
184  if ($resql) {
185  $num = $this->db->num_rows($resql);
186  if ($num) {
187  $obj = $this->db->fetch_object($resql);
188  $tiers = $obj->key_account;
189 
190  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']);
191 
192  try {
193  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
194  //$customer = \Stripe\Customer::retrieve("$tiers");
195  $customer = \Stripe\Customer::retrieve(array('id'=>"$tiers", 'expand[]'=>'sources'));
196  } else {
197  //$customer = \Stripe\Customer::retrieve("$tiers", array("stripe_account" => $key));
198  $customer = \Stripe\Customer::retrieve(array('id'=>"$tiers", 'expand[]'=>'sources'), array("stripe_account" => $key));
199  }
200  } catch (Exception $e) {
201  // For exemple, 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.'
202  $this->error = $e->getMessage();
203  }
204  } elseif ($createifnotlinkedtostripe) {
205  $ipaddress = getUserRemoteIP();
206 
207  $dataforcustomer = array(
208  "email" => $object->email,
209  "description" => $object->name,
210  "metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
211  );
212 
213  $vatcleaned = $object->tva_intra ? $object->tva_intra : null;
214 
215  /*
216  $taxinfo = array('type'=>'vat');
217  if ($vatcleaned)
218  {
219  $taxinfo["tax_id"] = $vatcleaned;
220  }
221  // We force data to "null" if not defined as expected by Stripe
222  if (empty($vatcleaned)) $taxinfo=null;
223  $dataforcustomer["tax_info"] = $taxinfo;
224  */
225 
226  //$a = \Stripe\Stripe::getApiKey();
227  //var_dump($a);var_dump($key);exit;
228  try {
229  // Force to use the correct API key
230  global $stripearrayofkeysbyenv;
231  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
232 
233  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
234  $customer = \Stripe\Customer::create($dataforcustomer);
235  } else {
236  $customer = \Stripe\Customer::create($dataforcustomer, array("stripe_account" => $key));
237  }
238 
239  // Create the VAT record in Stripe
240  if (!empty($conf->global->STRIPE_SAVE_TAX_IDS)) { // We setup to save Tax info on Stripe side. Warning: This may result in error when saving customer
241  if (!empty($vatcleaned)) {
242  $isineec = isInEEC($object);
243  if ($object->country_code && $isineec) {
244  //$taxids = $customer->allTaxIds($customer->id);
245  $customer->createTaxId($customer->id, array('type'=>'eu_vat', 'value'=>$vatcleaned));
246  }
247  }
248  }
249 
250  // Create customer in Dolibarr
251  $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_account (fk_soc, login, key_account, site, site_account, status, entity, date_creation, fk_user_creat)";
252  $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).")";
253  $resql = $this->db->query($sql);
254  if (!$resql) {
255  $this->error = $this->db->lasterror();
256  }
257  } catch (Exception $e) {
258  $this->error = $e->getMessage();
259  }
260  }
261  } else {
262  dol_print_error($this->db);
263  }
264 
265  return $customer;
266  }
267 
276  public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0)
277  {
278  $stripepaymentmethod = null;
279 
280  try {
281  // Force to use the correct API key
282  global $stripearrayofkeysbyenv;
283  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
284  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
285  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id);
286  } else {
287  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id, array("stripe_account" => $key));
288  }
289  } catch (Exception $e) {
290  $this->error = $e->getMessage();
291  }
292 
293  return $stripepaymentmethod;
294  }
295 
304  public function getSelectedReader($reader, $key = '', $status = 0)
305  {
306  $selectedreader = null;
307 
308  try {
309  // Force to use the correct API key
310  global $stripearrayofkeysbyenv;
311  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
312  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
313  $selectedreader = \Stripe\Terminal\Reader::retrieve((string) $reader);
314  } else {
315  $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve((string) $reader, array("stripe_account" => $key));
316  }
317  } catch (Exception $e) {
318  $this->error = $e->getMessage();
319  }
320 
321  return $selectedreader;
322  }
323 
350  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)
351  {
352  global $conf, $user;
353 
354  dol_syslog(get_class($this)."::getPaymentIntent", LOG_INFO, 1);
355 
356  $error = 0;
357 
358  if (empty($status)) {
359  $service = 'StripeTest';
360  } else {
361  $service = 'StripeLive';
362  }
363 
364  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
365  if (!in_array($currency_code, $arrayzerounitcurrency)) {
366  $stripeamount = $amount * 100;
367  } else {
368  $stripeamount = $amount;
369  }
370 
371  $fee = 0;
372  if (getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT")) {
373  $fee = $amount * ((float) getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT", '0') / 100) + (float) getDolGlobalString("STRIPE_APPLICATION_FEE", '0');
374  }
375  if ($fee >= (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') && (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') > (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
376  $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0');
377  } elseif ($fee < (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
378  $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0');
379  }
380  if (!in_array($currency_code, $arrayzerounitcurrency)) {
381  $stripefee = round($fee * 100);
382  } else {
383  $stripefee = round($fee);
384  }
385 
386  $paymentintent = null;
387 
388  if (is_object($object) && getDolGlobalInt('STRIPE_REUSE_EXISTING_INTENT_IF_FOUND') && !getDolGlobalInt('STRIPE_CARD_PRESENT')) {
389  // Warning. If a payment was tried and failed, a payment intent was created.
390  // 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.
391  // 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
392  // automatically return the existing payment intent if idempotency is provided when we try to create the new one.
393  // 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)
394 
395  $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
396  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
397  $sql .= " WHERE pi.fk_facture = ".((int) $object->id);
398  $sql .= " AND pi.sourcetype = '".$this->db->escape($object->element)."'";
399  $sql .= " AND pi.entity IN (".getEntity('societe').")";
400  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
401 
402  dol_syslog(get_class($this)."::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
403  $resql = $this->db->query($sql);
404  if ($resql) {
405  $num = $this->db->num_rows($resql);
406  if ($num) {
407  $obj = $this->db->fetch_object($resql);
408  $intent = $obj->ext_payment_id;
409 
410  dol_syslog(get_class($this)."::getPaymentIntent found existing payment intent record");
411 
412  // Force to use the correct API key
413  global $stripearrayofkeysbyenv;
414  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
415 
416  try {
417  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
418  $paymentintent = \Stripe\PaymentIntent::retrieve($intent);
419  } else {
420  $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
421  }
422  } catch (Exception $e) {
423  $error++;
424  $this->error = $e->getMessage();
425  }
426  }
427  }
428  }
429 
430  if (empty($paymentintent)) {
431  // Try to create intent. See https://stripe.com/docs/api/payment_intents/create
432  $ipaddress = getUserRemoteIP();
433  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
434  if (is_object($object)) {
435  $metadata['dol_type'] = $object->element;
436  $metadata['dol_id'] = $object->id;
437  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
438  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
439  }
440  }
441 
442  // list of payment method types
443  $paymentmethodtypes = array("card");
444  $descriptor = dol_trunc($tag, 10, 'right', 'UTF-8', 1);
445  if (getDolGlobalInt('STRIPE_SEPA_DIRECT_DEBIT')) {
446  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
447  //$descriptor = preg_replace('/ref=[^:=]+/', '', $descriptor); // Clean ref
448  }
449  if (getDolGlobalInt('STRIPE_KLARNA')) {
450  $paymentmethodtypes[] = "klarna";
451  }
452  if (getDolGlobalInt('STRIPE_BANCONTACT')) {
453  $paymentmethodtypes[] = "bancontact";
454  }
455  if (getDolGlobalInt('STRIPE_IDEAL')) {
456  $paymentmethodtypes[] = "ideal";
457  }
458  if (getDolGlobalInt('STRIPE_GIROPAY')) {
459  $paymentmethodtypes[] = "giropay";
460  }
461  if (getDolGlobalInt('STRIPE_SOFORT')) {
462  $paymentmethodtypes[] = "sofort";
463  }
464  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
465  $paymentmethodtypes = array("card_present");
466  }
467 
468  $dataforintent = array(
469  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
470  "confirmation_method" => $mode,
471  "amount" => $stripeamount,
472  "currency" => $currency_code,
473  "payment_method_types" => $paymentmethodtypes,
474  "description" => $description,
475  //"save_payment_method" => true,
476  "setup_future_usage" => "on_session",
477  "metadata" => $metadata
478  );
479  if ($descriptor) {
480  $dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
481  $dataforintent["statement_descriptor"] = $descriptor; // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
482  }
483  if (!is_null($customer)) {
484  $dataforintent["customer"] = $customer;
485  }
486  // payment_method =
487  // payment_method_types = array('card')
488  //var_dump($dataforintent);
489  if ($off_session) {
490  unset($dataforintent['setup_future_usage']);
491  // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
492  // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
493  //$dataforintent["setup_future_usage"] = "off_session";
494  $dataforintent["off_session"] = true;
495  }
496  if (getDolGlobalInt('STRIPE_GIROPAY')) {
497  unset($dataforintent['setup_future_usage']);
498  }
499  if (getDolGlobalInt('STRIPE_KLARNA')) {
500  unset($dataforintent['setup_future_usage']);
501  }
502  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
503  unset($dataforintent['setup_future_usage']);
504  $dataforintent["capture_method"] = "manual";
505  $dataforintent["confirmation_method"] = "manual";
506  }
507  if (!is_null($payment_method)) {
508  $dataforintent["payment_method"] = $payment_method;
509  $description .= ' - '.$payment_method;
510  }
511 
512  if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
513  $dataforintent["application_fee_amount"] = $stripefee;
514  }
515  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
516  $dataforintent["receipt_email"] = $object->thirdparty->email;
517  }
518 
519  try {
520  // Force to use the correct API key
521  global $stripearrayofkeysbyenv;
522  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
523 
524  $arrayofoptions = array();
525  if (empty($noidempotency_key)) {
526  $arrayofoptions["idempotency_key"] = $description;
527  }
528  // 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.
529  if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
530  $arrayofoptions["stripe_account"] = $key;
531  }
532 
533  dol_syslog("dataforintent to create paymentintent = ".var_export($dataforintent, true));
534 
535  $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
536 
537  // Store the payment intent
538  if (is_object($object)) {
539  $paymentintentalreadyexists = 0;
540 
541  if ($did > 0) {
542  // If a payment request line provided, we do not need to recreate one, we just update it
543  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
544 
545  $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
546  $sql .= " ext_payment_site = '".$this->db->escape($service)."',";
547  $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
548  $sql .= " WHERE rowid = ".((int) $did);
549 
550  $resql = $this->db->query($sql);
551  if ($resql) {
552  $paymentintentalreadyexists++;
553  } else {
554  $error++;
555  dol_print_error($this->db);
556  }
557  } else {
558  // Check that payment intent $paymentintent->id is not already recorded.
559  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
560 
561  $sql = "SELECT pi.rowid";
562  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
563  $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
564  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
565  $sql .= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
566 
567  $resql = $this->db->query($sql);
568  if ($resql) {
569  $num = $this->db->num_rows($resql);
570  if ($num) {
571  $obj = $this->db->fetch_object($resql);
572  if ($obj) {
573  $paymentintentalreadyexists++;
574  }
575  }
576  } else {
577  $error++;
578  dol_print_error($this->db);
579  }
580  }
581 
582  // If not, we create it.
583  if (!$error && !$paymentintentalreadyexists) {
584  $now = dol_now();
585  $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
586  $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).")";
587  $resql = $this->db->query($sql);
588  if (!$resql) {
589  $error++;
590  $this->error = $this->db->lasterror();
591  dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
592  }
593  }
594  } else {
595  $_SESSION["stripe_payment_intent"] = $paymentintent;
596  }
597  } catch (Stripe\Error\Card $e) {
598  $error++;
599  $this->error = $e->getMessage();
600  $this->code = $e->getStripeCode();
601  $this->declinecode = $e->getDeclineCode();
602  } catch (Exception $e) {
603  //var_dump($dataforintent);
604  //var_dump($description);
605  //var_dump($key);
606  //var_dump($paymentintent);
607  //var_dump($e->getMessage());
608  //var_dump($e);
609  $error++;
610  $this->error = $e->getMessage();
611  $this->code = '';
612  $this->declinecode = '';
613  }
614  }
615 
616  dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
617 
618  if (!$error) {
619  return $paymentintent;
620  } else {
621  return null;
622  }
623  }
624 
643  public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
644  {
645  global $conf;
646 
647  dol_syslog("getSetupIntent description=".$description.' confirmnow='.$confirmnow, LOG_INFO, 1);
648 
649  $error = 0;
650 
651  if (empty($status)) {
652  $service = 'StripeTest';
653  } else {
654  $service = 'StripeLive';
655  }
656 
657  $setupintent = null;
658 
659  if (empty($setupintent)) {
660  $ipaddress = getUserRemoteIP();
661  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
662  if (is_object($object)) {
663  $metadata['dol_type'] = $object->element;
664  $metadata['dol_id'] = $object->id;
665  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
666  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
667  }
668  }
669 
670  // list of payment method types
671  $paymentmethodtypes = array("card");
672  if (!empty($conf->global->STRIPE_SEPA_DIRECT_DEBIT)) {
673  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
674  }
675  if (!empty($conf->global->STRIPE_BANCONTACT)) {
676  $paymentmethodtypes[] = "bancontact";
677  }
678  if (!empty($conf->global->STRIPE_IDEAL)) {
679  $paymentmethodtypes[] = "ideal";
680  }
681  // Giropay not possible for setup intent
682  if (!empty($conf->global->STRIPE_SOFORT)) {
683  $paymentmethodtypes[] = "sofort";
684  }
685 
686  $dataforintent = array(
687  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
688  "payment_method_types" => $paymentmethodtypes,
689  "usage" => "off_session",
690  "metadata" => $metadata
691  );
692  if (!is_null($customer)) {
693  $dataforintent["customer"] = $customer;
694  }
695  if (!is_null($description)) {
696  $dataforintent["description"] = $description;
697  }
698  // payment_method =
699  // payment_method_types = array('card')
700  //var_dump($dataforintent);
701 
702  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
703  $dataforintent["receipt_email"] = $object->thirdparty->email;
704  }
705 
706  try {
707  // Force to use the correct API key
708  global $stripearrayofkeysbyenv;
709  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
710 
711  dol_syslog("getSetupIntent ".$stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG);
712 
713  // 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.
714  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
715  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
716  $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
717  } else {
718  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
719  $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
720  }
721  //var_dump($setupintent->id);
722 
723  // Store the setup intent
724  /*if (is_object($object))
725  {
726  $setupintentalreadyexists = 0;
727  // Check that payment intent $setupintent->id is not already recorded.
728  $sql = "SELECT pi.rowid";
729  $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
730  $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
731  $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
732  $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
733 
734  dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
735  $resql = $this->db->query($sql);
736  if ($resql) {
737  $num = $this->db->num_rows($resql);
738  if ($num)
739  {
740  $obj = $this->db->fetch_object($resql);
741  if ($obj) $setupintentalreadyexists++;
742  }
743  }
744  else dol_print_error($this->db);
745 
746  // If not, we create it.
747  if (! $setupintentalreadyexists)
748  {
749  $now=dol_now();
750  $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
751  $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).")";
752  $resql = $this->db->query($sql);
753  if (! $resql)
754  {
755  $error++;
756  $this->error = $this->db->lasterror();
757  dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
758  }
759  }
760  }
761  else
762  {
763  $_SESSION["stripe_setup_intent"] = $setupintent;
764  }*/
765  } catch (Exception $e) {
766  //var_dump($dataforintent);
767  //var_dump($description);
768  //var_dump($key);
769  //var_dump($setupintent);
770  //var_dump($e->getMessage());
771  $error++;
772  $this->error = $e->getMessage();
773  }
774  }
775 
776  if (!$error) {
777  dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
778  return $setupintent;
779  } else {
780  dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
781  return null;
782  }
783  }
784 
785 
796  public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
797  {
798  global $conf, $user, $langs;
799 
800  $card = null;
801 
802  $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_....
803  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
804  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
805  $sql .= " AND sa.type = 'card'";
806 
807  dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
808  $resql = $this->db->query($sql);
809  if ($resql) {
810  $num = $this->db->num_rows($resql);
811  if ($num) {
812  $obj = $this->db->fetch_object($resql);
813  $cardref = $obj->stripe_card_ref;
814  dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
815  if ($cardref) {
816  try {
817  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
818  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
819  $card = $cu->sources->retrieve($cardref);
820  } else {
821  $card = \Stripe\PaymentMethod::retrieve($cardref);
822  }
823  } else {
824  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
825  //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
826  $card = $cu->sources->retrieve($cardref);
827  } else {
828  //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
829  $card = \Stripe\PaymentMethod::retrieve($cardref);
830  }
831  }
832  } catch (Exception $e) {
833  $this->error = $e->getMessage();
834  dol_syslog($this->error, LOG_WARNING);
835  }
836  } elseif ($createifnotlinkedtostripe) {
837  // Deprecated with new Stripe API and SCA. We should not use anymore this part of code now.
838  $exp_date_month = $obj->exp_date_month;
839  $exp_date_year = $obj->exp_date_year;
840  $number = $obj->number;
841  $cvc = $obj->cvn; // cvn in database, cvc for stripe
842  $cardholdername = $obj->proprio;
843 
844  $ipaddress = getUserRemoteIP();
845 
846  $dataforcard = array(
847  "source" => array(
848  'object'=>'card',
849  'exp_month'=>$exp_date_month,
850  'exp_year'=>$exp_date_year,
851  'number'=>$number,
852  'cvc'=>$cvc,
853  'name'=>$cardholdername
854  ),
855  "metadata" => array(
856  'dol_type'=>$object->element,
857  'dol_id'=>$object->id,
858  'dol_version'=>DOL_VERSION,
859  'dol_entity'=>$conf->entity,
860  'ipaddress'=>$ipaddress
861  )
862  );
863 
864  //$a = \Stripe\Stripe::getApiKey();
865  //var_dump($a);
866  //var_dump($stripeacc);exit;
867  try {
868  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
869  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
870  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
871  $card = $cu->sources->create($dataforcard);
872  if (!$card) {
873  $this->error = 'Creation of card on Stripe has failed';
874  }
875  } else {
876  $connect = '';
877  if (!empty($stripeacc)) {
878  $connect = $stripeacc.'/';
879  }
880  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
881  if ($status) {
882  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
883  }
884  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
885 
886  //dol_syslog("Error: This case is not supported", LOG_ERR);
887  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
888  }
889  } else {
890  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
891  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
892  $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
893  if (!$card) {
894  $this->error = 'Creation of card on Stripe has failed';
895  }
896  } else {
897  $connect = '';
898  if (!empty($stripeacc)) {
899  $connect = $stripeacc.'/';
900  }
901  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
902  if ($status) {
903  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
904  }
905  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
906 
907  //dol_syslog("Error: This case is not supported", LOG_ERR);
908  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
909  }
910  }
911 
912  if ($card) {
913  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
914  $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
915  $sql .= " country_code = '".$this->db->escape($card->country)."',";
916  $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
917  $sql .= " WHERE rowid = ".((int) $object->id);
918  $sql .= " AND type = 'card'";
919  $resql = $this->db->query($sql);
920  if (!$resql) {
921  $this->error = $this->db->lasterror();
922  }
923  }
924  } catch (Exception $e) {
925  $this->error = $e->getMessage();
926  dol_syslog($this->error, LOG_WARNING);
927  }
928  }
929  }
930  } else {
931  dol_print_error($this->db);
932  }
933 
934  return $card;
935  }
936 
937 
948  public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
949  {
950  global $conf, $user, $langs;
951  $sepa = null;
952 
953  $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
954  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
955  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
956  $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
957 
958  $soc = new Societe($this->db);
959  $soc->fetch($object->fk_soc);
960 
961  dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
962  $resql = $this->db->query($sql);
963  if ($resql) {
964  $num = $this->db->num_rows($resql);
965  if ($num) {
966  $obj = $this->db->fetch_object($resql);
967  $cardref = $obj->stripe_card_ref;
968  dol_syslog(get_class($this)."::sepaStripe cardref=".$cardref);
969  if ($cardref) {
970  try {
971  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
972  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
973  $sepa = $cu->sources->retrieve($cardref);
974  } else {
975  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
976  }
977  } else {
978  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
979  //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
980  $sepa = $cu->sources->retrieve($cardref);
981  } else {
982  //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
983  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
984  }
985  }
986  } catch (Exception $e) {
987  $this->error = $e->getMessage();
988  dol_syslog($this->error, LOG_WARNING);
989  }
990  } elseif ($createifnotlinkedtostripe) {
991  $iban = $obj->iban;
992  $ipaddress = getUserRemoteIP();
993  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
994  if (is_object($object)) {
995  $metadata['dol_type'] = $object->element;
996  $metadata['dol_id'] = $object->id;
997  $metadata['dol_thirdparty_id'] = $soc->id;
998  }
999 
1000  $description = 'SEPA for IBAN '.$iban;
1001 
1002  $dataforcard = array(
1003  'type'=>'sepa_debit',
1004  "sepa_debit" => array('iban' => $iban),
1005  'billing_details' => array(
1006  'name' => $soc->name,
1007  'email' => !empty($soc->email) ? $soc->email : "",
1008  ),
1009  "metadata" => $metadata
1010  );
1011  // Complete owner name
1012  if (!empty($soc->town)) {
1013  $dataforcard['billing_details']['address']['city']=$soc->town;
1014  }
1015  if (!empty($soc->country_code)) {
1016  $dataforcard['billing_details']['address']['country']=$soc->country_code;
1017  }
1018  if (!empty($soc->address)) {
1019  $dataforcard['billing_details']['address']['line1']=$soc->address;
1020  }
1021  if (!empty($soc->zip)) {
1022  $dataforcard['billing_details']['address']['postal_code']=$soc->zip;
1023  }
1024  if (!empty($soc->state)) {
1025  $dataforcard['billing_details']['address']['state']=$soc->state;
1026  }
1027 
1028  //$a = \Stripe\Stripe::getApiKey();
1029  //var_dump($a);var_dump($stripeacc);exit;
1030  try {
1031  dol_syslog("Try to create sepa_debit 0");
1032 
1033  $service = 'StripeTest';
1034  $servicestatus = 0;
1035  if (!empty($conf->global->STRIPE_LIVE) && !GETPOST('forcesandbox', 'alpha')) {
1036  $service = 'StripeLive';
1037  $servicestatus = 1;
1038  }
1039  // Force to use the correct API key
1040  global $stripearrayofkeysbyenv;
1041  $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
1042 
1043  dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
1044 
1045  $s = new \Stripe\StripeClient($stripeacc);
1046 
1047  //var_dump($dataforcard);exit;
1048 
1049  $sepa = $s->paymentMethods->create($dataforcard);
1050  if (!$sepa) {
1051  $this->error = 'Creation of payment method sepa_debit on Stripe has failed';
1052  } else {
1053  // link customer and src
1054  //$cs = $this->getSetupIntent($description, $soc, $cu, '', $status);
1055  $dataforintent = array(['description'=> $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata'=>$metadata);
1056  $cs = $s->setupIntents->create($dataforintent);
1057  //$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]);
1058  $cs = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]);
1059  if (!$cs) {
1060  $this->error = 'Link SEPA <-> Customer failed';
1061  } else {
1062  dol_syslog("Update the payment mode of the customer");
1063  // print json_encode($sepa);
1064 
1065  // Save the Stripe payment mode ID into the Dolibarr database
1066  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
1067  $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."', card_type = 'sepa_debit',";
1068  $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "'";
1069  $sql .= " WHERE rowid = ".((int) $object->id);
1070  $sql .= " AND type = 'ban'";
1071  $resql = $this->db->query($sql);
1072  if (!$resql) {
1073  $this->error = $this->db->lasterror();
1074  }
1075  }
1076  }
1077  } catch (Exception $e) {
1078  $sepa = null;
1079  $this->error = $e->getMessage();
1080  dol_syslog($this->error, LOG_WARNING);
1081  }
1082  }
1083  }
1084  } else {
1085  dol_print_error($this->db);
1086  }
1087 
1088  return $sepa;
1089  }
1090 
1091 
1108  public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
1109  {
1110  global $conf;
1111 
1112  $error = 0;
1113 
1114  if (empty($status)) {
1115  $service = 'StripeTest';
1116  } else {
1117  $service = 'StripeLive';
1118  }
1119 
1120  $sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
1121  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
1122  $sql .= " WHERE sa.key_account = '".$this->db->escape($customer)."'";
1123  //$sql.= " AND sa.entity IN (".getEntity('societe').")";
1124  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
1125 
1126  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1127  $result = $this->db->query($sql);
1128  if ($result) {
1129  if ($this->db->num_rows($result)) {
1130  $obj = $this->db->fetch_object($result);
1131  $key = $obj->fk_soc;
1132  } else {
1133  $key = null;
1134  }
1135  } else {
1136  $key = null;
1137  }
1138 
1139  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
1140  if (!in_array($currency, $arrayzerounitcurrency)) {
1141  $stripeamount = $amount * 100;
1142  } else {
1143  $stripeamount = $amount;
1144  }
1145 
1146  $societe = new Societe($this->db);
1147  if ($key > 0) {
1148  $societe->fetch($key);
1149  }
1150 
1151  $description = "";
1152  $ref = "";
1153  if ($origin == 'order') {
1154  $order = new Commande($this->db);
1155  $order->fetch($item);
1156  $ref = $order->ref;
1157  $description = "ORD=".$ref.".CUS=".$societe->id.".PM=stripe";
1158  } elseif ($origin == 'invoice') {
1159  $invoice = new Facture($this->db);
1160  $invoice->fetch($item);
1161  $ref = $invoice->ref;
1162  $description = "INV=".$ref.".CUS=".$societe->id.".PM=stripe";
1163  }
1164 
1165  $ipaddress = getUserRemoteIP();
1166 
1167  $metadata = array(
1168  "dol_id" => (string) $item,
1169  "dol_type" => (string) $origin,
1170  "dol_thirdparty_id" => (string) $societe->id,
1171  'dol_thirdparty_name' => $societe->name,
1172  'dol_version' => DOL_VERSION,
1173  'dol_entity' => $conf->entity,
1174  'ipaddress' => $ipaddress
1175  );
1176  $return = new Stripe($this->db);
1177  try {
1178  // Force to use the correct API key
1179  global $stripearrayofkeysbyenv;
1180  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
1181 
1182  if (empty($conf->stripeconnect->enabled)) { // With a common Stripe account
1183  if (preg_match('/pm_/i', $source)) {
1184  $stripecard = $source;
1185  $amountstripe = $stripeamount;
1186  $FULLTAG = 'PFBO'; // Payment From Back Office
1187  $stripe = $return;
1188  $amounttopay = $amount;
1189  $servicestatus = $status;
1190 
1191  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1192  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1193 
1194  dol_syslog("* createPaymentStripe Create payment for customer ".$customer->id." on source card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1195 
1196  // Create payment intent and charge payment (confirmnow = true)
1197  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1198 
1199  $charge = new stdClass();
1200  if ($paymentintent->status == 'succeeded') {
1201  $charge->status = 'ok';
1202  } else {
1203  $charge->status = 'failed';
1204  $charge->failure_code = $stripe->code;
1205  $charge->failure_message = $stripe->error;
1206  $charge->failure_declinecode = $stripe->declinecode;
1207  $stripefailurecode = $stripe->code;
1208  $stripefailuremessage = $stripe->error;
1209  $stripefailuredeclinecode = $stripe->declinecode;
1210  }
1211  } elseif (preg_match('/acct_/i', $source)) {
1212  $charge = \Stripe\Charge::create(array(
1213  "amount" => "$stripeamount",
1214  "currency" => "$currency",
1215  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1216  "description" => "Stripe payment: ".$description,
1217  "capture" => $capture,
1218  "metadata" => $metadata,
1219  "source" => "$source"
1220  ));
1221  } else {
1222  $paymentarray = array(
1223  "amount" => "$stripeamount",
1224  "currency" => "$currency",
1225  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1226  "description" => "Stripe payment: ".$description,
1227  "capture" => $capture,
1228  "metadata" => $metadata,
1229  "source" => "$source",
1230  "customer" => "$customer"
1231  );
1232 
1233  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1234  $paymentarray["receipt_email"] = $societe->email;
1235  }
1236 
1237  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
1238  }
1239  } else {
1240  // With Stripe Connect
1241  $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
1242  if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1243  $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
1244  } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1245  $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
1246  }
1247 
1248  if (!in_array($currency, $arrayzerounitcurrency)) {
1249  $stripefee = round($fee * 100);
1250  } else {
1251  $stripefee = round($fee);
1252  }
1253 
1254  $paymentarray = array(
1255  "amount" => "$stripeamount",
1256  "currency" => "$currency",
1257  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1258  "description" => "Stripe payment: ".$description,
1259  "capture" => $capture,
1260  "metadata" => $metadata,
1261  "source" => "$source",
1262  "customer" => "$customer"
1263  );
1264  if ($conf->entity != $conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0) {
1265  $paymentarray["application_fee_amount"] = $stripefee;
1266  }
1267  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1268  $paymentarray["receipt_email"] = $societe->email;
1269  }
1270 
1271  if (preg_match('/pm_/i', $source)) {
1272  $stripecard = $source;
1273  $amountstripe = $stripeamount;
1274  $FULLTAG = 'PFBO'; // Payment From Back Office
1275  $stripe = $return;
1276  $amounttopay = $amount;
1277  $servicestatus = $status;
1278 
1279  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1280  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1281 
1282  dol_syslog("* createPaymentStripe Create payment on card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1283 
1284  // Create payment intent and charge payment (confirmnow = true)
1285  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1286 
1287  $charge = new stdClass();
1288  if ($paymentintent->status == 'succeeded') {
1289  $charge->status = 'ok';
1290  $charge->id = $paymentintent->id;
1291  } else {
1292  $charge->status = 'failed';
1293  $charge->failure_code = $stripe->code;
1294  $charge->failure_message = $stripe->error;
1295  $charge->failure_declinecode = $stripe->declinecode;
1296  }
1297  } else {
1298  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
1299  }
1300  }
1301  if (isset($charge->id)) {
1302  }
1303 
1304  $return->statut = 'success';
1305  $return->id = $charge->id;
1306 
1307  if (preg_match('/pm_/i', $source)) {
1308  $return->message = 'Payment retrieved by card status = '.$charge->status;
1309  } else {
1310  if ($charge->source->type == 'card') {
1311  $return->message = $charge->source->card->brand." ....".$charge->source->card->last4;
1312  } elseif ($charge->source->type == 'three_d_secure') {
1313  $stripe = new Stripe($this->db);
1314  $src = \Stripe\Source::retrieve("".$charge->source->three_d_secure->card, array(
1315  "stripe_account" => $stripe->getStripeAccount($service)
1316  ));
1317  $return->message = $src->card->brand." ....".$src->card->last4;
1318  } else {
1319  $return->message = $charge->id;
1320  }
1321  }
1322  } catch (\Stripe\Error\Card $e) {
1323  include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
1324  // Since it's a decline, \Stripe\Error\Card will be caught
1325  $body = $e->getJsonBody();
1326  $err = $body['error'];
1327 
1328  $return->statut = 'error';
1329  $return->id = $err['charge'];
1330  $return->type = $err['type'];
1331  $return->code = $err['code'];
1332  $return->message = $err['message'];
1333  $body = "Error: <br>".$return->id." ".$return->message." ";
1334  $subject = '[Alert] Payment error using Stripe';
1335  $cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
1336  $cmailfile->sendfile();
1337 
1338  $error++;
1339  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1340  } catch (\Stripe\Error\RateLimit $e) {
1341  // Too many requests made to the API too quickly
1342  $error++;
1343  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1344  } catch (\Stripe\Error\InvalidRequest $e) {
1345  // Invalid parameters were supplied to Stripe's API
1346  $error++;
1347  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1348  } catch (\Stripe\Error\Authentication $e) {
1349  // Authentication with Stripe's API failed
1350  // (maybe you changed API keys recently)
1351  $error++;
1352  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1353  } catch (\Stripe\Error\ApiConnection $e) {
1354  // Network communication with Stripe failed
1355  $error++;
1356  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1357  } catch (\Stripe\Error\Base $e) {
1358  // Display a very generic error to the user, and maybe send
1359  // yourself an email
1360  $error++;
1361  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1362  } catch (Exception $e) {
1363  // Something else happened, completely unrelated to Stripe
1364  $error++;
1365  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1366  }
1367  return $return;
1368  }
1369 }
Stripe\customerStripe
customerStripe(Societe $object, $key='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe customer of a thirdparty (with option to create it in Stripe if not linked yet).
Definition: stripe.class.php:159
Societe
Class to manage third parties objects (customers, suppliers, prospects...)
Definition: societe.class.php:51
Stripe\getStripeAccount
getStripeAccount($mode='StripeTest', $fk_soc=0, $entity=-1)
Return main company OAuth Connect stripe account.
Definition: stripe.class.php:89
dol_trunc
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.
Definition: functions.lib.php:4059
Stripe\sepaStripe
sepaStripe($cu, CompanyPaymentMode $object, $stripeacc='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe SEPA of a company payment mode.
Definition: stripe.class.php:948
Stripe\__construct
__construct($db)
Constructor.
Definition: stripe.class.php:75
GETPOST
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
Definition: functions.lib.php:609
Stripe\createPaymentStripe
createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status=0, $usethirdpartyemailforreceiptemail=0, $capture=true)
Create charge.
Definition: stripe.class.php:1108
dol_print_error
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
Definition: functions.lib.php:5107
SocieteAccount
Class for SocieteAccount.
Definition: societeaccount.class.php:35
Stripe\getSetupIntent
getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail=0, $confirmnow=false)
Get the Stripe payment intent.
Definition: stripe.class.php:643
CMailFile
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Definition: CMailFile.class.php:41
Facture
Class to manage invoices.
Definition: facture.class.php:60
CommonObject
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Definition: commonobject.class.php:45
Stripe\getPaymentIntent
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.
Definition: stripe.class.php:350
Stripe\getSelectedReader
getSelectedReader($reader, $key='', $status=0)
Get the Stripe reader Object from its ID.
Definition: stripe.class.php:304
Stripe\getStripeCustomerAccount
getStripeCustomerAccount($id, $status=0, $site_account='')
getStripeCustomerAccount
Definition: stripe.class.php:141
img_picto
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
Definition: functions.lib.php:4135
Exception
code
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...
Definition: sync_members_ldap2dolibarr.php:60
Commande
Class to manage customers orders.
Definition: commande.class.php:47
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1741
Stripe\cardStripe
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 ...
Definition: stripe.class.php:796
$sql
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->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') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
getDolGlobalString
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
Definition: functions.lib.php:142
Stripe\getPaymentMethodStripe
getPaymentMethodStripe($paymentmethod, $key='', $status=0)
Get the Stripe payment method Object from its ID.
Definition: stripe.class.php:276
dol_now
dol_now($mode='auto')
Return date for now.
Definition: functions.lib.php:3056
Stripe
Stripe class.
Definition: stripe.class.php:29
CompanyPaymentMode
Class for CompanyPaymentMode.
Definition: companypaymentmode.class.php:33
getDolGlobalInt
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
Definition: functions.lib.php:156
getUserRemoteIP
getUserRemoteIP()
Return the IP of remote user.
Definition: functions.lib.php:3742
isInEEC
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
Definition: company.lib.php:785
float
div float
Buy price without taxes.
Definition: style.css.php:921
if
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
Definition: journals_list.php:25