dolibarr  x.y.z
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  $tmparray = json_decode($tokenstring);
118  $key = empty($tmparray->stripe_user_id) ? '' : $tmparray->stripe_user_id;
119  } else {
120  $tokenstring = '';
121  }
122  } else {
123  dol_print_error($this->db);
124  }
125 
126  dol_syslog("No dedicated Stripe Connect account available for entity ".$conf->entity);
127  return $key;
128  }
129 
138  public function getStripeCustomerAccount($id, $status = 0, $site_account = '')
139  {
140  include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
141  $societeaccount = new SocieteAccount($this->db);
142  return $societeaccount->getCustomerAccount($id, 'stripe', $status, $site_account); // Get thirdparty cus_...
143  }
144 
145 
156  public function customerStripe(Societe $object, $key = '', $status = 0, $createifnotlinkedtostripe = 0)
157  {
158  global $conf, $user;
159 
160  if (empty($object->id)) {
161  dol_syslog("customerStripe is called with the parameter object that is not loaded");
162  return null;
163  }
164 
165  $customer = null;
166 
167  // Force to use the correct API key
168  global $stripearrayofkeysbyenv;
169  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
170 
171  $sql = "SELECT sa.key_account as key_account, sa.entity"; // key_account is cus_....
172  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
173  $sql .= " WHERE sa.fk_soc = ".((int) $object->id);
174  $sql .= " AND sa.entity IN (".getEntity('societe').")";
175  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
176  $sql .= " AND (sa.site_account IS NULL OR sa.site_account = '' OR sa.site_account = '".$this->db->escape($stripearrayofkeysbyenv[$status]['publishable_key'])."')";
177  $sql .= " AND sa.key_account IS NOT NULL AND sa.key_account <> ''";
178 
179  dol_syslog(get_class($this)."::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG);
180  $resql = $this->db->query($sql);
181  if ($resql) {
182  $num = $this->db->num_rows($resql);
183  if ($num) {
184  $obj = $this->db->fetch_object($resql);
185  $tiers = $obj->key_account;
186 
187  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']);
188 
189  try {
190  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
191  //$customer = \Stripe\Customer::retrieve("$tiers");
192  $customer = \Stripe\Customer::retrieve(array('id'=>"$tiers", 'expand[]'=>'sources'));
193  } else {
194  //$customer = \Stripe\Customer::retrieve("$tiers", array("stripe_account" => $key));
195  $customer = \Stripe\Customer::retrieve(array('id'=>"$tiers", 'expand[]'=>'sources'), array("stripe_account" => $key));
196  }
197  } catch (Exception $e) {
198  // 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.'
199  $this->error = $e->getMessage();
200  }
201  } elseif ($createifnotlinkedtostripe) {
202  $ipaddress = getUserRemoteIP();
203 
204  $dataforcustomer = array(
205  "email" => $object->email,
206  "description" => $object->name,
207  "metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
208  );
209 
210  $vatcleaned = $object->tva_intra ? $object->tva_intra : null;
211 
212  /*
213  $taxinfo = array('type'=>'vat');
214  if ($vatcleaned)
215  {
216  $taxinfo["tax_id"] = $vatcleaned;
217  }
218  // We force data to "null" if not defined as expected by Stripe
219  if (empty($vatcleaned)) $taxinfo=null;
220  $dataforcustomer["tax_info"] = $taxinfo;
221  */
222 
223  //$a = \Stripe\Stripe::getApiKey();
224  //var_dump($a);var_dump($key);exit;
225  try {
226  // Force to use the correct API key
227  global $stripearrayofkeysbyenv;
228  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
229 
230  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
231  $customer = \Stripe\Customer::create($dataforcustomer);
232  } else {
233  $customer = \Stripe\Customer::create($dataforcustomer, array("stripe_account" => $key));
234  }
235 
236  // Create the VAT record in Stripe
237  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
238  if (!empty($vatcleaned)) {
239  $isineec = isInEEC($object);
240  if ($object->country_code && $isineec) {
241  //$taxids = $customer->allTaxIds($customer->id);
242  $customer->createTaxId($customer->id, array('type'=>'eu_vat', 'value'=>$vatcleaned));
243  }
244  }
245  }
246 
247  // Create customer in Dolibarr
248  $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_account (fk_soc, login, key_account, site, site_account, status, entity, date_creation, fk_user_creat)";
249  $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).")";
250  $resql = $this->db->query($sql);
251  if (!$resql) {
252  $this->error = $this->db->lasterror();
253  }
254  } catch (Exception $e) {
255  $this->error = $e->getMessage();
256  }
257  }
258  } else {
259  dol_print_error($this->db);
260  }
261 
262  return $customer;
263  }
264 
273  public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0)
274  {
275  $stripepaymentmethod = null;
276 
277  try {
278  // Force to use the correct API key
279  global $stripearrayofkeysbyenv;
280  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
281  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
282  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id);
283  } else {
284  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id, array("stripe_account" => $key));
285  }
286  } catch (Exception $e) {
287  $this->error = $e->getMessage();
288  }
289 
290  return $stripepaymentmethod;
291  }
292 
301  public function getSelectedReader($reader, $key = '', $status = 0)
302  {
303  $selectedreader = null;
304 
305  try {
306  // Force to use the correct API key
307  global $stripearrayofkeysbyenv;
308  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
309  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
310  $selectedreader = \Stripe\Terminal\Reader::retrieve((string) $reader);
311  } else {
312  $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve((string) $reader, array("stripe_account" => $key));
313  }
314  } catch (Exception $e) {
315  $this->error = $e->getMessage();
316  }
317 
318  return $selectedreader;
319  }
320 
346  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)
347  {
348  global $conf, $user;
349 
350  dol_syslog(get_class($this)."::getPaymentIntent", LOG_INFO, 1);
351 
352  $error = 0;
353 
354  if (empty($status)) {
355  $service = 'StripeTest';
356  } else {
357  $service = 'StripeLive';
358  }
359 
360  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
361  if (!in_array($currency_code, $arrayzerounitcurrency)) {
362  $stripeamount = $amount * 100;
363  } else {
364  $stripeamount = $amount;
365  }
366 
367  $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
368  if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
369  $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
370  } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
371  $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
372  }
373  if (!in_array($currency_code, $arrayzerounitcurrency)) {
374  $stripefee = round($fee * 100);
375  } else {
376  $stripefee = round($fee);
377  }
378 
379  $paymentintent = null;
380 
381  if (is_object($object) && getDolGlobalInt('STRIPE_REUSE_EXISTING_INTENT_IF_FOUND') && !getDolGlobalInt('STRIPE_CARD_PRESENT')) {
382  // Warning. If a payment was tried and failed, a payment intent was created.
383  // 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.
384  // 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
385  // automatically return the existing payment intent if idempotency is provided when we try to create the new one.
386  // 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)
387 
388  $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
389  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
390  $sql .= " WHERE pi.fk_facture = ".((int) $object->id);
391  $sql .= " AND pi.sourcetype = '".$this->db->escape($object->element)."'";
392  $sql .= " AND pi.entity IN (".getEntity('societe').")";
393  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
394 
395  dol_syslog(get_class($this)."::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
396  $resql = $this->db->query($sql);
397  if ($resql) {
398  $num = $this->db->num_rows($resql);
399  if ($num) {
400  $obj = $this->db->fetch_object($resql);
401  $intent = $obj->ext_payment_id;
402 
403  dol_syslog(get_class($this)."::getPaymentIntent found existing payment intent record");
404 
405  // Force to use the correct API key
406  global $stripearrayofkeysbyenv;
407  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
408 
409  try {
410  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
411  $paymentintent = \Stripe\PaymentIntent::retrieve($intent);
412  } else {
413  $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
414  }
415  } catch (Exception $e) {
416  $error++;
417  $this->error = $e->getMessage();
418  }
419  }
420  }
421  }
422 
423  if (empty($paymentintent)) {
424  // Try to create intent. See https://stripe.com/docs/api/payment_intents/create
425  $ipaddress = getUserRemoteIP();
426  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
427  if (is_object($object)) {
428  $metadata['dol_type'] = $object->element;
429  $metadata['dol_id'] = $object->id;
430  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
431  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
432  }
433  }
434 
435  // list of payment method types
436  $paymentmethodtypes = array("card");
437  $descriptor = dol_trunc($tag, 10, 'right', 'UTF-8', 1);
438  if (getDolGlobalInt('STRIPE_SEPA_DIRECT_DEBIT')) {
439  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
440  //$descriptor = preg_replace('/ref=[^:=]+/', '', $descriptor); // Clean ref
441  }
442  if (getDolGlobalInt('STRIPE_KLARNA')) {
443  $paymentmethodtypes[] = "klarna";
444  }
445  if (getDolGlobalInt('STRIPE_BANCONTACT')) {
446  $paymentmethodtypes[] = "bancontact";
447  }
448  if (getDolGlobalInt('STRIPE_IDEAL')) {
449  $paymentmethodtypes[] = "ideal";
450  }
451  if (getDolGlobalInt('STRIPE_GIROPAY')) {
452  $paymentmethodtypes[] = "giropay";
453  }
454  if (getDolGlobalInt('STRIPE_SOFORT')) {
455  $paymentmethodtypes[] = "sofort";
456  }
457  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
458  $paymentmethodtypes = array("card_present");
459  }
460 
461  $dataforintent = array(
462  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
463  "confirmation_method" => $mode,
464  "amount" => $stripeamount,
465  "currency" => $currency_code,
466  "payment_method_types" => $paymentmethodtypes,
467  "description" => $description,
468  "statement_descriptor_suffix" => $descriptor, // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
469  "statement_descriptor" => $descriptor, // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
470  //"save_payment_method" => true,
471  "setup_future_usage" => "on_session",
472  "metadata" => $metadata
473  );
474  if (!is_null($customer)) {
475  $dataforintent["customer"] = $customer;
476  }
477  // payment_method =
478  // payment_method_types = array('card')
479  //var_dump($dataforintent);
480  if ($off_session) {
481  unset($dataforintent['setup_future_usage']);
482  // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
483  // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
484  //$dataforintent["setup_future_usage"] = "off_session";
485  $dataforintent["off_session"] = true;
486  }
487  if (getDolGlobalInt('STRIPE_GIROPAY')) {
488  unset($dataforintent['setup_future_usage']);
489  }
490  if (getDolGlobalInt('STRIPE_KLARNA')) {
491  unset($dataforintent['setup_future_usage']);
492  }
493  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
494  unset($dataforintent['setup_future_usage']);
495  $dataforintent["capture_method"] = "manual";
496  $dataforintent["confirmation_method"] = "manual";
497  }
498  if (!is_null($payment_method)) {
499  $dataforintent["payment_method"] = $payment_method;
500  $description .= ' - '.$payment_method;
501  }
502 
503  if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
504  $dataforintent["application_fee_amount"] = $stripefee;
505  }
506  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
507  $dataforintent["receipt_email"] = $object->thirdparty->email;
508  }
509 
510  try {
511  // Force to use the correct API key
512  global $stripearrayofkeysbyenv;
513  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
514 
515  $arrayofoptions = array();
516  if (empty($noidempotency_key)) {
517  $arrayofoptions["idempotency_key"] = $description;
518  }
519  // 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.
520  if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
521  $arrayofoptions["stripe_account"] = $key;
522  }
523 
524  dol_syslog("dataforintent to create paymentintent = ".var_export($dataforintent, true));
525 
526  $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
527 
528  // Store the payment intent
529  if (is_object($object)) {
530  $paymentintentalreadyexists = 0;
531  // Check that payment intent $paymentintent->id is not already recorded.
532  $sql = "SELECT pi.rowid";
533  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
534  $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
535  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
536  $sql .= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
537 
538  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
539  $resql = $this->db->query($sql);
540  if ($resql) {
541  $num = $this->db->num_rows($resql);
542  if ($num) {
543  $obj = $this->db->fetch_object($resql);
544  if ($obj) {
545  $paymentintentalreadyexists++;
546  }
547  }
548  } else {
549  dol_print_error($this->db);
550  }
551 
552  // If not, we create it.
553  if (!$paymentintentalreadyexists) {
554  $now = dol_now();
555  $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
556  $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).")";
557  $resql = $this->db->query($sql);
558  if (!$resql) {
559  $error++;
560  $this->error = $this->db->lasterror();
561  dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
562  }
563  }
564  } else {
565  $_SESSION["stripe_payment_intent"] = $paymentintent;
566  }
567  } catch (Stripe\Error\Card $e) {
568  $error++;
569  $this->error = $e->getMessage();
570  $this->code = $e->getStripeCode();
571  $this->declinecode = $e->getDeclineCode();
572  } catch (Exception $e) {
573  //var_dump($dataforintent);
574  //var_dump($description);
575  //var_dump($key);
576  //var_dump($paymentintent);
577  //var_dump($e->getMessage());
578  //var_dump($e);
579  $error++;
580  $this->error = $e->getMessage();
581  $this->code = '';
582  $this->declinecode = '';
583  }
584  }
585 
586  dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
587 
588  if (!$error) {
589  return $paymentintent;
590  } else {
591  return null;
592  }
593  }
594 
613  public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
614  {
615  global $conf;
616 
617  dol_syslog("getSetupIntent description=".$description.' confirmnow='.$confirmnow, LOG_INFO, 1);
618 
619  $error = 0;
620 
621  if (empty($status)) {
622  $service = 'StripeTest';
623  } else {
624  $service = 'StripeLive';
625  }
626 
627  $setupintent = null;
628 
629  if (empty($setupintent)) {
630  $ipaddress = getUserRemoteIP();
631  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
632  if (is_object($object)) {
633  $metadata['dol_type'] = $object->element;
634  $metadata['dol_id'] = $object->id;
635  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
636  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
637  }
638  }
639 
640  // list of payment method types
641  $paymentmethodtypes = array("card");
642  if (!empty($conf->global->STRIPE_SEPA_DIRECT_DEBIT)) {
643  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
644  }
645  if (!empty($conf->global->STRIPE_BANCONTACT)) {
646  $paymentmethodtypes[] = "bancontact";
647  }
648  if (!empty($conf->global->STRIPE_IDEAL)) {
649  $paymentmethodtypes[] = "ideal";
650  }
651  // Giropay not possible for setup intent
652  if (!empty($conf->global->STRIPE_SOFORT)) {
653  $paymentmethodtypes[] = "sofort";
654  }
655 
656  $dataforintent = array(
657  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
658  "payment_method_types" => $paymentmethodtypes,
659  "usage" => "off_session",
660  "metadata" => $metadata
661  );
662  if (!is_null($customer)) {
663  $dataforintent["customer"] = $customer;
664  }
665  if (!is_null($description)) {
666  $dataforintent["description"] = $description;
667  }
668  // payment_method =
669  // payment_method_types = array('card')
670  //var_dump($dataforintent);
671 
672  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
673  $dataforintent["receipt_email"] = $object->thirdparty->email;
674  }
675 
676  try {
677  // Force to use the correct API key
678  global $stripearrayofkeysbyenv;
679  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
680 
681  dol_syslog("getSetupIntent ".$stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG);
682 
683  // 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.
684  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
685  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
686  $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
687  } else {
688  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
689  $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
690  }
691  //var_dump($setupintent->id);
692 
693  // Store the setup intent
694  /*if (is_object($object))
695  {
696  $setupintentalreadyexists = 0;
697  // Check that payment intent $setupintent->id is not already recorded.
698  $sql = "SELECT pi.rowid";
699  $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
700  $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
701  $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
702  $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
703 
704  dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
705  $resql = $this->db->query($sql);
706  if ($resql) {
707  $num = $this->db->num_rows($resql);
708  if ($num)
709  {
710  $obj = $this->db->fetch_object($resql);
711  if ($obj) $setupintentalreadyexists++;
712  }
713  }
714  else dol_print_error($this->db);
715 
716  // If not, we create it.
717  if (! $setupintentalreadyexists)
718  {
719  $now=dol_now();
720  $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
721  $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).")";
722  $resql = $this->db->query($sql);
723  if (! $resql)
724  {
725  $error++;
726  $this->error = $this->db->lasterror();
727  dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
728  }
729  }
730  }
731  else
732  {
733  $_SESSION["stripe_setup_intent"] = $setupintent;
734  }*/
735  } catch (Exception $e) {
736  //var_dump($dataforintent);
737  //var_dump($description);
738  //var_dump($key);
739  //var_dump($setupintent);
740  //var_dump($e->getMessage());
741  $error++;
742  $this->error = $e->getMessage();
743  }
744  }
745 
746  if (!$error) {
747  dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
748  return $setupintent;
749  } else {
750  dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
751  return null;
752  }
753  }
754 
755 
766  public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
767  {
768  global $conf, $user, $langs;
769 
770  $card = null;
771 
772  $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_....
773  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
774  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
775  $sql .= " AND sa.type = 'card'";
776 
777  dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
778  $resql = $this->db->query($sql);
779  if ($resql) {
780  $num = $this->db->num_rows($resql);
781  if ($num) {
782  $obj = $this->db->fetch_object($resql);
783  $cardref = $obj->stripe_card_ref;
784  dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
785  if ($cardref) {
786  try {
787  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
788  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
789  $card = $cu->sources->retrieve($cardref);
790  } else {
791  $card = \Stripe\PaymentMethod::retrieve($cardref);
792  }
793  } else {
794  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
795  //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
796  $card = $cu->sources->retrieve($cardref);
797  } else {
798  //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
799  $card = \Stripe\PaymentMethod::retrieve($cardref);
800  }
801  }
802  } catch (Exception $e) {
803  $this->error = $e->getMessage();
804  dol_syslog($this->error, LOG_WARNING);
805  }
806  } elseif ($createifnotlinkedtostripe) {
807  $exp_date_month = $obj->exp_date_month;
808  $exp_date_year = $obj->exp_date_year;
809  $number = $obj->number;
810  $cvc = $obj->cvn; // cvn in database, cvc for stripe
811  $cardholdername = $obj->proprio;
812 
813  $ipaddress = getUserRemoteIP();
814 
815  $dataforcard = array(
816  "source" => array('object'=>'card', 'exp_month'=>$exp_date_month, 'exp_year'=>$exp_date_year, 'number'=>$number, 'cvc'=>$cvc, 'name'=>$cardholdername),
817  "metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
818  );
819 
820  //$a = \Stripe\Stripe::getApiKey();
821  //var_dump($a);
822  //var_dump($stripeacc);exit;
823  try {
824  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
825  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
826  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
827  $card = $cu->sources->create($dataforcard);
828  if (!$card) {
829  $this->error = 'Creation of card on Stripe has failed';
830  }
831  } else {
832  $connect = '';
833  if (!empty($stripeacc)) {
834  $connect = $stripeacc.'/';
835  }
836  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
837  if ($status) {
838  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
839  }
840  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
841 
842  //dol_syslog("Error: This case is not supported", LOG_ERR);
843  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
844  }
845  } else {
846  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
847  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
848  $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
849  if (!$card) {
850  $this->error = 'Creation of card on Stripe has failed';
851  }
852  } else {
853  $connect = '';
854  if (!empty($stripeacc)) {
855  $connect = $stripeacc.'/';
856  }
857  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
858  if ($status) {
859  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
860  }
861  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
862 
863  //dol_syslog("Error: This case is not supported", LOG_ERR);
864  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
865  }
866  }
867 
868  if ($card) {
869  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
870  $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
871  $sql .= " country_code = '".$this->db->escape($card->country)."',";
872  $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
873  $sql .= " WHERE rowid = ".((int) $object->id);
874  $sql .= " AND type = 'card'";
875  $resql = $this->db->query($sql);
876  if (!$resql) {
877  $this->error = $this->db->lasterror();
878  }
879  }
880  } catch (Exception $e) {
881  $this->error = $e->getMessage();
882  dol_syslog($this->error, LOG_WARNING);
883  }
884  }
885  }
886  } else {
887  dol_print_error($this->db);
888  }
889 
890  return $card;
891  }
892 
893 
904  public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
905  {
906  global $conf, $user, $langs;
907  $sepa = null;
908 
909  $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix"; // stripe_card_ref is src_ for sepa
910  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
911  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
912  $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
913 
914  $soc = new Societe($this->db);
915  $soc->fetch($object->fk_soc);
916 
917  dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
918  $resql = $this->db->query($sql);
919  if ($resql) {
920  $num = $this->db->num_rows($resql);
921  if ($num) {
922  $obj = $this->db->fetch_object($resql);
923  $cardref = $obj->stripe_card_ref;
924  dol_syslog(get_class($this)."::sepaStripe cardref=".$cardref);
925  if ($cardref) {
926  try {
927  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
928  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
929  $sepa = $cu->sources->retrieve($cardref);
930  } else {
931  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
932  }
933  } else {
934  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
935  //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
936  $sepa = $cu->sources->retrieve($cardref);
937  } else {
938  //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
939  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
940  }
941  }
942  } catch (Exception $e) {
943  $this->error = $e->getMessage();
944  dol_syslog($this->error, LOG_WARNING);
945  }
946  } elseif ($createifnotlinkedtostripe) {
947  $iban = $obj->iban_prefix; //prefix ?
948  $ipaddress = getUserRemoteIP();
949 
950  $dataforcard = array(
951  'type'=>'sepa_debit',
952  "sepa_debit" => array('iban' => $iban),
953  'currency' => 'eur',
954  'usage' => 'reusable',
955  'owner' => array(
956  'name' => $soc->name,
957  ),
958  "metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
959  );
960 
961  //$a = \Stripe\Stripe::getApiKey();
962  //var_dump($a);var_dump($stripeacc);exit;
963  try {
964  dol_syslog("Try to create sepa_debit 0");
965 
966  $service = 'StripeTest';
967  $servicestatus = 0;
968  if (!empty($conf->global->STRIPE_LIVE) && !GETPOST('forcesandbox', 'alpha')) {
969  $service = 'StripeLive';
970  $servicestatus = 1;
971  }
972  // Force to use the correct API key
973  global $stripearrayofkeysbyenv;
974  $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
975 
976  dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
977  $s = new \Stripe\StripeClient($stripeacc);
978  $sepa = $s->sources->create($dataforcard);
979  if (!$sepa) {
980  $this->error = 'Creation of sepa_debit on Stripe has failed';
981  } else {
982  // association du client avec cette source de paimeent
983  $cs = $cu->createSource($cu->id, array('source' => $sepa->id));
984  if (!$cs) {
985  $this->error = 'Link SEPA <-> Customer failed';
986  } else {
987  dol_syslog("Try to create sepa_debit 3");
988  // print json_encode($sepa);
989 
990  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
991  $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."', card_type = 'sepa_debit',";
992  $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "'";
993  $sql .= " WHERE rowid = ".((int) $object->id);
994  $sql .= " AND type = 'ban'";
995  $resql = $this->db->query($sql);
996  if (!$resql) {
997  $this->error = $this->db->lasterror();
998  }
999  }
1000  }
1001  } catch (Exception $e) {
1002  $this->error = $e->getMessage();
1003  dol_syslog($this->error, LOG_WARNING);
1004  }
1005  }
1006  }
1007  } else {
1008  dol_print_error($this->db);
1009  }
1010 
1011  return $sepa;
1012  }
1013 
1030  public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
1031  {
1032  global $conf;
1033 
1034  $error = 0;
1035 
1036  if (empty($status)) {
1037  $service = 'StripeTest';
1038  } else {
1039  $service = 'StripeLive';
1040  }
1041 
1042  $sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
1043  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
1044  $sql .= " WHERE sa.key_account = '".$this->db->escape($customer)."'";
1045  //$sql.= " AND sa.entity IN (".getEntity('societe').")";
1046  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
1047 
1048  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1049  $result = $this->db->query($sql);
1050  if ($result) {
1051  if ($this->db->num_rows($result)) {
1052  $obj = $this->db->fetch_object($result);
1053  $key = $obj->fk_soc;
1054  } else {
1055  $key = null;
1056  }
1057  } else {
1058  $key = null;
1059  }
1060 
1061  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
1062  if (!in_array($currency, $arrayzerounitcurrency)) {
1063  $stripeamount = $amount * 100;
1064  } else {
1065  $stripeamount = $amount;
1066  }
1067 
1068  $societe = new Societe($this->db);
1069  if ($key > 0) {
1070  $societe->fetch($key);
1071  }
1072 
1073  $description = "";
1074  $ref = "";
1075  if ($origin == 'order') {
1076  $order = new Commande($this->db);
1077  $order->fetch($item);
1078  $ref = $order->ref;
1079  $description = "ORD=".$ref.".CUS=".$societe->id.".PM=stripe";
1080  } elseif ($origin == 'invoice') {
1081  $invoice = new Facture($this->db);
1082  $invoice->fetch($item);
1083  $ref = $invoice->ref;
1084  $description = "INV=".$ref.".CUS=".$societe->id.".PM=stripe";
1085  }
1086 
1087  $ipaddress = getUserRemoteIP();
1088 
1089  $metadata = array(
1090  "dol_id" => (string) $item,
1091  "dol_type" => (string) $origin,
1092  "dol_thirdparty_id" => (string) $societe->id,
1093  'dol_thirdparty_name' => $societe->name,
1094  'dol_version' => DOL_VERSION,
1095  'dol_entity' => $conf->entity,
1096  'ipaddress' => $ipaddress
1097  );
1098  $return = new Stripe($this->db);
1099  try {
1100  // Force to use the correct API key
1101  global $stripearrayofkeysbyenv;
1102  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
1103 
1104  if (empty($conf->stripeconnect->enabled)) { // With a common Stripe account
1105  if (preg_match('/pm_/i', $source)) {
1106  $stripecard = $source;
1107  $amountstripe = $stripeamount;
1108  $FULLTAG = 'PFBO'; // Payment From Back Office
1109  $stripe = $return;
1110  $amounttopay = $amount;
1111  $servicestatus = $status;
1112 
1113  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1114  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1115 
1116  dol_syslog("* createPaymentStripe Create payment for customer ".$customer->id." on source card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1117 
1118  // Create payment intent and charge payment (confirmnow = true)
1119  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1120 
1121  $charge = new stdClass();
1122  if ($paymentintent->status == 'succeeded') {
1123  $charge->status = 'ok';
1124  } else {
1125  $charge->status = 'failed';
1126  $charge->failure_code = $stripe->code;
1127  $charge->failure_message = $stripe->error;
1128  $charge->failure_declinecode = $stripe->declinecode;
1129  $stripefailurecode = $stripe->code;
1130  $stripefailuremessage = $stripe->error;
1131  $stripefailuredeclinecode = $stripe->declinecode;
1132  }
1133  } elseif (preg_match('/acct_/i', $source)) {
1134  $charge = \Stripe\Charge::create(array(
1135  "amount" => "$stripeamount",
1136  "currency" => "$currency",
1137  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1138  "description" => "Stripe payment: ".$description,
1139  "capture" => $capture,
1140  "metadata" => $metadata,
1141  "source" => "$source"
1142  ));
1143  } else {
1144  $paymentarray = array(
1145  "amount" => "$stripeamount",
1146  "currency" => "$currency",
1147  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1148  "description" => "Stripe payment: ".$description,
1149  "capture" => $capture,
1150  "metadata" => $metadata,
1151  "source" => "$source",
1152  "customer" => "$customer"
1153  );
1154 
1155  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1156  $paymentarray["receipt_email"] = $societe->email;
1157  }
1158 
1159  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
1160  }
1161  } else {
1162  // With Stripe Connect
1163  $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
1164  if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1165  $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
1166  } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1167  $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
1168  }
1169 
1170  if (!in_array($currency, $arrayzerounitcurrency)) {
1171  $stripefee = round($fee * 100);
1172  } else {
1173  $stripefee = round($fee);
1174  }
1175 
1176  $paymentarray = array(
1177  "amount" => "$stripeamount",
1178  "currency" => "$currency",
1179  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1180  "description" => "Stripe payment: ".$description,
1181  "capture" => $capture,
1182  "metadata" => $metadata,
1183  "source" => "$source",
1184  "customer" => "$customer"
1185  );
1186  if ($conf->entity != $conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0) {
1187  $paymentarray["application_fee_amount"] = $stripefee;
1188  }
1189  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1190  $paymentarray["receipt_email"] = $societe->email;
1191  }
1192 
1193  if (preg_match('/pm_/i', $source)) {
1194  $stripecard = $source;
1195  $amountstripe = $stripeamount;
1196  $FULLTAG = 'PFBO'; // Payment From Back Office
1197  $stripe = $return;
1198  $amounttopay = $amount;
1199  $servicestatus = $status;
1200 
1201  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1202  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1203 
1204  dol_syslog("* createPaymentStripe Create payment on card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1205 
1206  // Create payment intent and charge payment (confirmnow = true)
1207  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1208 
1209  $charge = new stdClass();
1210  if ($paymentintent->status == 'succeeded') {
1211  $charge->status = 'ok';
1212  $charge->id = $paymentintent->id;
1213  } else {
1214  $charge->status = 'failed';
1215  $charge->failure_code = $stripe->code;
1216  $charge->failure_message = $stripe->error;
1217  $charge->failure_declinecode = $stripe->declinecode;
1218  }
1219  } else {
1220  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
1221  }
1222  }
1223  if (isset($charge->id)) {
1224  }
1225 
1226  $return->statut = 'success';
1227  $return->id = $charge->id;
1228 
1229  if (preg_match('/pm_/i', $source)) {
1230  $return->message = 'Payment retrieved by card status = '.$charge->status;
1231  } else {
1232  if ($charge->source->type == 'card') {
1233  $return->message = $charge->source->card->brand." ....".$charge->source->card->last4;
1234  } elseif ($charge->source->type == 'three_d_secure') {
1235  $stripe = new Stripe($this->db);
1236  $src = \Stripe\Source::retrieve("".$charge->source->three_d_secure->card, array(
1237  "stripe_account" => $stripe->getStripeAccount($service)
1238  ));
1239  $return->message = $src->card->brand." ....".$src->card->last4;
1240  } else {
1241  $return->message = $charge->id;
1242  }
1243  }
1244  } catch (\Stripe\Error\Card $e) {
1245  include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
1246  // Since it's a decline, \Stripe\Error\Card will be caught
1247  $body = $e->getJsonBody();
1248  $err = $body['error'];
1249 
1250  $return->statut = 'error';
1251  $return->id = $err['charge'];
1252  $return->type = $err['type'];
1253  $return->code = $err['code'];
1254  $return->message = $err['message'];
1255  $body = "Error: <br>".$return->id." ".$return->message." ";
1256  $subject = '[Alert] Payment error using Stripe';
1257  $cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
1258  $cmailfile->sendfile();
1259 
1260  $error++;
1261  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1262  } catch (\Stripe\Error\RateLimit $e) {
1263  // Too many requests made to the API too quickly
1264  $error++;
1265  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1266  } catch (\Stripe\Error\InvalidRequest $e) {
1267  // Invalid parameters were supplied to Stripe's API
1268  $error++;
1269  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1270  } catch (\Stripe\Error\Authentication $e) {
1271  // Authentication with Stripe's API failed
1272  // (maybe you changed API keys recently)
1273  $error++;
1274  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1275  } catch (\Stripe\Error\ApiConnection $e) {
1276  // Network communication with Stripe failed
1277  $error++;
1278  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1279  } catch (\Stripe\Error\Base $e) {
1280  // Display a very generic error to the user, and maybe send
1281  // yourself an email
1282  $error++;
1283  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1284  } catch (Exception $e) {
1285  // Something else happened, completely unrelated to Stripe
1286  $error++;
1287  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1288  }
1289  return $return;
1290  }
1291 }
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.
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)
Get the Stripe payment intent.
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.
getStripeCustomerAccount($id, $status=0, $site_account='')
getStripeCustomerAccount
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.
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).
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('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
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.
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:913
$conf db
API class for accounts.
Definition: inc.php:41
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...