dolibarr  18.0.0-alpha
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 
347  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)
348  {
349  global $conf, $user;
350 
351  dol_syslog(get_class($this)."::getPaymentIntent", LOG_INFO, 1);
352 
353  $error = 0;
354 
355  if (empty($status)) {
356  $service = 'StripeTest';
357  } else {
358  $service = 'StripeLive';
359  }
360 
361  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
362  if (!in_array($currency_code, $arrayzerounitcurrency)) {
363  $stripeamount = $amount * 100;
364  } else {
365  $stripeamount = $amount;
366  }
367 
368  $fee = 0;
369  if (getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT")) {
370  $fee = $amount * ((float) getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT", '0') / 100) + (float) getDolGlobalString("STRIPE_APPLICATION_FEE", '0');
371  }
372  if ($fee >= (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') && (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') > (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
373  $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0');
374  } elseif ($fee < (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
375  $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0');
376  }
377  if (!in_array($currency_code, $arrayzerounitcurrency)) {
378  $stripefee = round($fee * 100);
379  } else {
380  $stripefee = round($fee);
381  }
382 
383  $paymentintent = null;
384 
385  if (is_object($object) && getDolGlobalInt('STRIPE_REUSE_EXISTING_INTENT_IF_FOUND') && !getDolGlobalInt('STRIPE_CARD_PRESENT')) {
386  // Warning. If a payment was tried and failed, a payment intent was created.
387  // 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.
388  // 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
389  // automatically return the existing payment intent if idempotency is provided when we try to create the new one.
390  // 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)
391 
392  $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
393  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
394  $sql .= " WHERE pi.fk_facture = ".((int) $object->id);
395  $sql .= " AND pi.sourcetype = '".$this->db->escape($object->element)."'";
396  $sql .= " AND pi.entity IN (".getEntity('societe').")";
397  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
398 
399  dol_syslog(get_class($this)."::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
400  $resql = $this->db->query($sql);
401  if ($resql) {
402  $num = $this->db->num_rows($resql);
403  if ($num) {
404  $obj = $this->db->fetch_object($resql);
405  $intent = $obj->ext_payment_id;
406 
407  dol_syslog(get_class($this)."::getPaymentIntent found existing payment intent record");
408 
409  // Force to use the correct API key
410  global $stripearrayofkeysbyenv;
411  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
412 
413  try {
414  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
415  $paymentintent = \Stripe\PaymentIntent::retrieve($intent);
416  } else {
417  $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
418  }
419  } catch (Exception $e) {
420  $error++;
421  $this->error = $e->getMessage();
422  }
423  }
424  }
425  }
426 
427  if (empty($paymentintent)) {
428  // Try to create intent. See https://stripe.com/docs/api/payment_intents/create
429  $ipaddress = getUserRemoteIP();
430  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
431  if (is_object($object)) {
432  $metadata['dol_type'] = $object->element;
433  $metadata['dol_id'] = $object->id;
434  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
435  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
436  }
437  }
438 
439  // list of payment method types
440  $paymentmethodtypes = array("card");
441  $descriptor = dol_trunc($tag, 10, 'right', 'UTF-8', 1);
442  if (getDolGlobalInt('STRIPE_SEPA_DIRECT_DEBIT')) {
443  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
444  //$descriptor = preg_replace('/ref=[^:=]+/', '', $descriptor); // Clean ref
445  }
446  if (getDolGlobalInt('STRIPE_KLARNA')) {
447  $paymentmethodtypes[] = "klarna";
448  }
449  if (getDolGlobalInt('STRIPE_BANCONTACT')) {
450  $paymentmethodtypes[] = "bancontact";
451  }
452  if (getDolGlobalInt('STRIPE_IDEAL')) {
453  $paymentmethodtypes[] = "ideal";
454  }
455  if (getDolGlobalInt('STRIPE_GIROPAY')) {
456  $paymentmethodtypes[] = "giropay";
457  }
458  if (getDolGlobalInt('STRIPE_SOFORT')) {
459  $paymentmethodtypes[] = "sofort";
460  }
461  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
462  $paymentmethodtypes = array("card_present");
463  }
464 
465  $dataforintent = array(
466  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
467  "confirmation_method" => $mode,
468  "amount" => $stripeamount,
469  "currency" => $currency_code,
470  "payment_method_types" => $paymentmethodtypes,
471  "description" => $description,
472  //"save_payment_method" => true,
473  "setup_future_usage" => "on_session",
474  "metadata" => $metadata
475  );
476  if ($descriptor) {
477  $dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
478  $dataforintent["statement_descriptor"] = $descriptor; // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
479  }
480  if (!is_null($customer)) {
481  $dataforintent["customer"] = $customer;
482  }
483  // payment_method =
484  // payment_method_types = array('card')
485  //var_dump($dataforintent);
486  if ($off_session) {
487  unset($dataforintent['setup_future_usage']);
488  // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
489  // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
490  //$dataforintent["setup_future_usage"] = "off_session";
491  $dataforintent["off_session"] = true;
492  }
493  if (getDolGlobalInt('STRIPE_GIROPAY')) {
494  unset($dataforintent['setup_future_usage']);
495  }
496  if (getDolGlobalInt('STRIPE_KLARNA')) {
497  unset($dataforintent['setup_future_usage']);
498  }
499  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
500  unset($dataforintent['setup_future_usage']);
501  $dataforintent["capture_method"] = "manual";
502  $dataforintent["confirmation_method"] = "manual";
503  }
504  if (!is_null($payment_method)) {
505  $dataforintent["payment_method"] = $payment_method;
506  $description .= ' - '.$payment_method;
507  }
508 
509  if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
510  $dataforintent["application_fee_amount"] = $stripefee;
511  }
512  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
513  $dataforintent["receipt_email"] = $object->thirdparty->email;
514  }
515 
516  try {
517  // Force to use the correct API key
518  global $stripearrayofkeysbyenv;
519  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
520 
521  $arrayofoptions = array();
522  if (empty($noidempotency_key)) {
523  $arrayofoptions["idempotency_key"] = $description;
524  }
525  // 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.
526  if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
527  $arrayofoptions["stripe_account"] = $key;
528  }
529 
530  dol_syslog("dataforintent to create paymentintent = ".var_export($dataforintent, true));
531 
532  $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
533 
534  // Store the payment intent
535  if (is_object($object)) {
536  $paymentintentalreadyexists = 0;
537 
538  if ($did > 0) {
539  // If a payment request line provided, we do not need to recreate one, we just update it
540  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
541 
542  $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
543  $sql .= " ext_payment_site = '".$this->db->escape($service)."',";
544  $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
545  $sql .= " WHERE rowid = ".((int) $did);
546 
547  $resql = $this->db->query($sql);
548  if ($resql) {
549  $paymentintentalreadyexists++;
550  } else {
551  $error++;
552  dol_print_error($this->db);
553  }
554  } else {
555  // Check that payment intent $paymentintent->id is not already recorded.
556  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
557 
558  $sql = "SELECT pi.rowid";
559  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
560  $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
561  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
562  $sql .= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
563 
564  $resql = $this->db->query($sql);
565  if ($resql) {
566  $num = $this->db->num_rows($resql);
567  if ($num) {
568  $obj = $this->db->fetch_object($resql);
569  if ($obj) {
570  $paymentintentalreadyexists++;
571  }
572  }
573  } else {
574  $error++;
575  dol_print_error($this->db);
576  }
577  }
578 
579  // If not, we create it.
580  if (!$error && !$paymentintentalreadyexists) {
581  $now = dol_now();
582  $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
583  $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).")";
584  $resql = $this->db->query($sql);
585  if (!$resql) {
586  $error++;
587  $this->error = $this->db->lasterror();
588  dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
589  }
590  }
591  } else {
592  $_SESSION["stripe_payment_intent"] = $paymentintent;
593  }
594  } catch (Stripe\Error\Card $e) {
595  $error++;
596  $this->error = $e->getMessage();
597  $this->code = $e->getStripeCode();
598  $this->declinecode = $e->getDeclineCode();
599  } catch (Exception $e) {
600  //var_dump($dataforintent);
601  //var_dump($description);
602  //var_dump($key);
603  //var_dump($paymentintent);
604  //var_dump($e->getMessage());
605  //var_dump($e);
606  $error++;
607  $this->error = $e->getMessage();
608  $this->code = '';
609  $this->declinecode = '';
610  }
611  }
612 
613  dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
614 
615  if (!$error) {
616  return $paymentintent;
617  } else {
618  return null;
619  }
620  }
621 
640  public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
641  {
642  global $conf;
643 
644  dol_syslog("getSetupIntent description=".$description.' confirmnow='.$confirmnow, LOG_INFO, 1);
645 
646  $error = 0;
647 
648  if (empty($status)) {
649  $service = 'StripeTest';
650  } else {
651  $service = 'StripeLive';
652  }
653 
654  $setupintent = null;
655 
656  if (empty($setupintent)) {
657  $ipaddress = getUserRemoteIP();
658  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
659  if (is_object($object)) {
660  $metadata['dol_type'] = $object->element;
661  $metadata['dol_id'] = $object->id;
662  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
663  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
664  }
665  }
666 
667  // list of payment method types
668  $paymentmethodtypes = array("card");
669  if (!empty($conf->global->STRIPE_SEPA_DIRECT_DEBIT)) {
670  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
671  }
672  if (!empty($conf->global->STRIPE_BANCONTACT)) {
673  $paymentmethodtypes[] = "bancontact";
674  }
675  if (!empty($conf->global->STRIPE_IDEAL)) {
676  $paymentmethodtypes[] = "ideal";
677  }
678  // Giropay not possible for setup intent
679  if (!empty($conf->global->STRIPE_SOFORT)) {
680  $paymentmethodtypes[] = "sofort";
681  }
682 
683  $dataforintent = array(
684  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
685  "payment_method_types" => $paymentmethodtypes,
686  "usage" => "off_session",
687  "metadata" => $metadata
688  );
689  if (!is_null($customer)) {
690  $dataforintent["customer"] = $customer;
691  }
692  if (!is_null($description)) {
693  $dataforintent["description"] = $description;
694  }
695  // payment_method =
696  // payment_method_types = array('card')
697  //var_dump($dataforintent);
698 
699  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
700  $dataforintent["receipt_email"] = $object->thirdparty->email;
701  }
702 
703  try {
704  // Force to use the correct API key
705  global $stripearrayofkeysbyenv;
706  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
707 
708  dol_syslog("getSetupIntent ".$stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG);
709 
710  // 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.
711  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
712  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
713  $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
714  } else {
715  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
716  $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
717  }
718  //var_dump($setupintent->id);
719 
720  // Store the setup intent
721  /*if (is_object($object))
722  {
723  $setupintentalreadyexists = 0;
724  // Check that payment intent $setupintent->id is not already recorded.
725  $sql = "SELECT pi.rowid";
726  $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
727  $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
728  $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
729  $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
730 
731  dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
732  $resql = $this->db->query($sql);
733  if ($resql) {
734  $num = $this->db->num_rows($resql);
735  if ($num)
736  {
737  $obj = $this->db->fetch_object($resql);
738  if ($obj) $setupintentalreadyexists++;
739  }
740  }
741  else dol_print_error($this->db);
742 
743  // If not, we create it.
744  if (! $setupintentalreadyexists)
745  {
746  $now=dol_now();
747  $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
748  $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).")";
749  $resql = $this->db->query($sql);
750  if (! $resql)
751  {
752  $error++;
753  $this->error = $this->db->lasterror();
754  dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
755  }
756  }
757  }
758  else
759  {
760  $_SESSION["stripe_setup_intent"] = $setupintent;
761  }*/
762  } catch (Exception $e) {
763  //var_dump($dataforintent);
764  //var_dump($description);
765  //var_dump($key);
766  //var_dump($setupintent);
767  //var_dump($e->getMessage());
768  $error++;
769  $this->error = $e->getMessage();
770  }
771  }
772 
773  if (!$error) {
774  dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
775  return $setupintent;
776  } else {
777  dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
778  return null;
779  }
780  }
781 
782 
793  public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
794  {
795  global $conf, $user, $langs;
796 
797  $card = null;
798 
799  $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_....
800  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
801  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
802  $sql .= " AND sa.type = 'card'";
803 
804  dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
805  $resql = $this->db->query($sql);
806  if ($resql) {
807  $num = $this->db->num_rows($resql);
808  if ($num) {
809  $obj = $this->db->fetch_object($resql);
810  $cardref = $obj->stripe_card_ref;
811  dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
812  if ($cardref) {
813  try {
814  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
815  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
816  $card = $cu->sources->retrieve($cardref);
817  } else {
818  $card = \Stripe\PaymentMethod::retrieve($cardref);
819  }
820  } else {
821  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
822  //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
823  $card = $cu->sources->retrieve($cardref);
824  } else {
825  //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
826  $card = \Stripe\PaymentMethod::retrieve($cardref);
827  }
828  }
829  } catch (Exception $e) {
830  $this->error = $e->getMessage();
831  dol_syslog($this->error, LOG_WARNING);
832  }
833  } elseif ($createifnotlinkedtostripe) {
834  // Deprecated with new Stripe API and SCA. We should not use anymore this part of code now.
835  $exp_date_month = $obj->exp_date_month;
836  $exp_date_year = $obj->exp_date_year;
837  $number = $obj->number;
838  $cvc = $obj->cvn; // cvn in database, cvc for stripe
839  $cardholdername = $obj->proprio;
840 
841  $ipaddress = getUserRemoteIP();
842 
843  $dataforcard = array(
844  "source" => array(
845  'object'=>'card',
846  'exp_month'=>$exp_date_month,
847  'exp_year'=>$exp_date_year,
848  'number'=>$number,
849  'cvc'=>$cvc,
850  'name'=>$cardholdername
851  ),
852  "metadata" => array(
853  'dol_type'=>$object->element,
854  'dol_id'=>$object->id,
855  'dol_version'=>DOL_VERSION,
856  'dol_entity'=>$conf->entity,
857  'ipaddress'=>$ipaddress
858  )
859  );
860 
861  //$a = \Stripe\Stripe::getApiKey();
862  //var_dump($a);
863  //var_dump($stripeacc);exit;
864  try {
865  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
866  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
867  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
868  $card = $cu->sources->create($dataforcard);
869  if (!$card) {
870  $this->error = 'Creation of card on Stripe has failed';
871  }
872  } else {
873  $connect = '';
874  if (!empty($stripeacc)) {
875  $connect = $stripeacc.'/';
876  }
877  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
878  if ($status) {
879  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
880  }
881  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
882 
883  //dol_syslog("Error: This case is not supported", LOG_ERR);
884  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
885  }
886  } else {
887  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
888  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
889  $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
890  if (!$card) {
891  $this->error = 'Creation of card on Stripe has failed';
892  }
893  } else {
894  $connect = '';
895  if (!empty($stripeacc)) {
896  $connect = $stripeacc.'/';
897  }
898  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
899  if ($status) {
900  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
901  }
902  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
903 
904  //dol_syslog("Error: This case is not supported", LOG_ERR);
905  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
906  }
907  }
908 
909  if ($card) {
910  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
911  $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
912  $sql .= " country_code = '".$this->db->escape($card->country)."',";
913  $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
914  $sql .= " WHERE rowid = ".((int) $object->id);
915  $sql .= " AND type = 'card'";
916  $resql = $this->db->query($sql);
917  if (!$resql) {
918  $this->error = $this->db->lasterror();
919  }
920  }
921  } catch (Exception $e) {
922  $this->error = $e->getMessage();
923  dol_syslog($this->error, LOG_WARNING);
924  }
925  }
926  }
927  } else {
928  dol_print_error($this->db);
929  }
930 
931  return $card;
932  }
933 
934 
945  public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
946  {
947  global $conf, $user, $langs;
948  $sepa = null;
949 
950  $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
951  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
952  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
953  $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
954 
955  $soc = new Societe($this->db);
956  $soc->fetch($object->fk_soc);
957 
958  dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
959  $resql = $this->db->query($sql);
960  if ($resql) {
961  $num = $this->db->num_rows($resql);
962  if ($num) {
963  $obj = $this->db->fetch_object($resql);
964  $cardref = $obj->stripe_card_ref;
965  dol_syslog(get_class($this)."::sepaStripe cardref=".$cardref);
966  if ($cardref) {
967  try {
968  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
969  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
970  $sepa = $cu->sources->retrieve($cardref);
971  } else {
972  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
973  }
974  } else {
975  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
976  //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
977  $sepa = $cu->sources->retrieve($cardref);
978  } else {
979  //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
980  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
981  }
982  }
983  } catch (Exception $e) {
984  $this->error = $e->getMessage();
985  dol_syslog($this->error, LOG_WARNING);
986  }
987  } elseif ($createifnotlinkedtostripe) {
988  $iban = $obj->iban;
989  $ipaddress = getUserRemoteIP();
990  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
991  if (is_object($object)) {
992  $metadata['dol_type'] = $object->element;
993  $metadata['dol_id'] = $object->id;
994  $metadata['dol_thirdparty_id'] = $soc->id;
995  }
996 
997  $description = 'SEPA for IBAN '.$iban;
998 
999  $dataforcard = array(
1000  'type'=>'sepa_debit',
1001  "sepa_debit" => array('iban' => $iban),
1002  'billing_details' => array(
1003  'name' => $soc->name,
1004  'email' => !empty($soc->email) ? $soc->email : "",
1005  ),
1006  "metadata" => $metadata
1007  );
1008  // Complete owner name
1009  if (!empty($soc->town)) {
1010  $dataforcard['billing_details']['address']['city']=$soc->town;
1011  }
1012  if (!empty($soc->country_code)) {
1013  $dataforcard['billing_details']['address']['country']=$soc->country_code;
1014  }
1015  if (!empty($soc->address)) {
1016  $dataforcard['billing_details']['address']['line1']=$soc->address;
1017  }
1018  if (!empty($soc->zip)) {
1019  $dataforcard['billing_details']['address']['postal_code']=$soc->zip;
1020  }
1021  if (!empty($soc->state)) {
1022  $dataforcard['billing_details']['address']['state']=$soc->state;
1023  }
1024 
1025  //$a = \Stripe\Stripe::getApiKey();
1026  //var_dump($a);var_dump($stripeacc);exit;
1027  try {
1028  dol_syslog("Try to create sepa_debit 0");
1029 
1030  $service = 'StripeTest';
1031  $servicestatus = 0;
1032  if (!empty($conf->global->STRIPE_LIVE) && !GETPOST('forcesandbox', 'alpha')) {
1033  $service = 'StripeLive';
1034  $servicestatus = 1;
1035  }
1036  // Force to use the correct API key
1037  global $stripearrayofkeysbyenv;
1038  $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
1039 
1040  dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
1041 
1042  $s = new \Stripe\StripeClient($stripeacc);
1043 
1044  //var_dump($dataforcard);exit;
1045 
1046  $sepa = $s->paymentMethods->create($dataforcard);
1047  if (!$sepa) {
1048  $this->error = 'Creation of payment method sepa_debit on Stripe has failed';
1049  } else {
1050  // link customer and src
1051  //$cs = $this->getSetupIntent($description, $soc, $cu, '', $status);
1052  $dataforintent = array(['description'=> $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata'=>$metadata);
1053  $cs = $s->setupIntents->create($dataforintent);
1054  //$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]);
1055  $cs = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]);
1056  if (!$cs) {
1057  $this->error = 'Link SEPA <-> Customer failed';
1058  } else {
1059  dol_syslog("Update the payment mode of the customer");
1060  // print json_encode($sepa);
1061 
1062  // Save the Stripe payment mode ID into the Dolibarr database
1063  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
1064  $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."', card_type = 'sepa_debit',";
1065  $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "'";
1066  $sql .= " WHERE rowid = ".((int) $object->id);
1067  $sql .= " AND type = 'ban'";
1068  $resql = $this->db->query($sql);
1069  if (!$resql) {
1070  $this->error = $this->db->lasterror();
1071  }
1072  }
1073  }
1074  } catch (Exception $e) {
1075  $sepa = null;
1076  $this->error = $e->getMessage();
1077  dol_syslog($this->error, LOG_WARNING);
1078  }
1079  }
1080  }
1081  } else {
1082  dol_print_error($this->db);
1083  }
1084 
1085  return $sepa;
1086  }
1087 
1088 
1105  public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
1106  {
1107  global $conf;
1108 
1109  $error = 0;
1110 
1111  if (empty($status)) {
1112  $service = 'StripeTest';
1113  } else {
1114  $service = 'StripeLive';
1115  }
1116 
1117  $sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
1118  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
1119  $sql .= " WHERE sa.key_account = '".$this->db->escape($customer)."'";
1120  //$sql.= " AND sa.entity IN (".getEntity('societe').")";
1121  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
1122 
1123  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1124  $result = $this->db->query($sql);
1125  if ($result) {
1126  if ($this->db->num_rows($result)) {
1127  $obj = $this->db->fetch_object($result);
1128  $key = $obj->fk_soc;
1129  } else {
1130  $key = null;
1131  }
1132  } else {
1133  $key = null;
1134  }
1135 
1136  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
1137  if (!in_array($currency, $arrayzerounitcurrency)) {
1138  $stripeamount = $amount * 100;
1139  } else {
1140  $stripeamount = $amount;
1141  }
1142 
1143  $societe = new Societe($this->db);
1144  if ($key > 0) {
1145  $societe->fetch($key);
1146  }
1147 
1148  $description = "";
1149  $ref = "";
1150  if ($origin == 'order') {
1151  $order = new Commande($this->db);
1152  $order->fetch($item);
1153  $ref = $order->ref;
1154  $description = "ORD=".$ref.".CUS=".$societe->id.".PM=stripe";
1155  } elseif ($origin == 'invoice') {
1156  $invoice = new Facture($this->db);
1157  $invoice->fetch($item);
1158  $ref = $invoice->ref;
1159  $description = "INV=".$ref.".CUS=".$societe->id.".PM=stripe";
1160  }
1161 
1162  $ipaddress = getUserRemoteIP();
1163 
1164  $metadata = array(
1165  "dol_id" => (string) $item,
1166  "dol_type" => (string) $origin,
1167  "dol_thirdparty_id" => (string) $societe->id,
1168  'dol_thirdparty_name' => $societe->name,
1169  'dol_version' => DOL_VERSION,
1170  'dol_entity' => $conf->entity,
1171  'ipaddress' => $ipaddress
1172  );
1173  $return = new Stripe($this->db);
1174  try {
1175  // Force to use the correct API key
1176  global $stripearrayofkeysbyenv;
1177  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
1178 
1179  if (empty($conf->stripeconnect->enabled)) { // With a common Stripe account
1180  if (preg_match('/pm_/i', $source)) {
1181  $stripecard = $source;
1182  $amountstripe = $stripeamount;
1183  $FULLTAG = 'PFBO'; // Payment From Back Office
1184  $stripe = $return;
1185  $amounttopay = $amount;
1186  $servicestatus = $status;
1187 
1188  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1189  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1190 
1191  dol_syslog("* createPaymentStripe Create payment for customer ".$customer->id." on source card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1192 
1193  // Create payment intent and charge payment (confirmnow = true)
1194  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1195 
1196  $charge = new stdClass();
1197  if ($paymentintent->status == 'succeeded') {
1198  $charge->status = 'ok';
1199  } else {
1200  $charge->status = 'failed';
1201  $charge->failure_code = $stripe->code;
1202  $charge->failure_message = $stripe->error;
1203  $charge->failure_declinecode = $stripe->declinecode;
1204  $stripefailurecode = $stripe->code;
1205  $stripefailuremessage = $stripe->error;
1206  $stripefailuredeclinecode = $stripe->declinecode;
1207  }
1208  } elseif (preg_match('/acct_/i', $source)) {
1209  $charge = \Stripe\Charge::create(array(
1210  "amount" => "$stripeamount",
1211  "currency" => "$currency",
1212  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1213  "description" => "Stripe payment: ".$description,
1214  "capture" => $capture,
1215  "metadata" => $metadata,
1216  "source" => "$source"
1217  ));
1218  } else {
1219  $paymentarray = array(
1220  "amount" => "$stripeamount",
1221  "currency" => "$currency",
1222  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1223  "description" => "Stripe payment: ".$description,
1224  "capture" => $capture,
1225  "metadata" => $metadata,
1226  "source" => "$source",
1227  "customer" => "$customer"
1228  );
1229 
1230  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1231  $paymentarray["receipt_email"] = $societe->email;
1232  }
1233 
1234  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
1235  }
1236  } else {
1237  // With Stripe Connect
1238  $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
1239  if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1240  $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
1241  } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1242  $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
1243  }
1244 
1245  if (!in_array($currency, $arrayzerounitcurrency)) {
1246  $stripefee = round($fee * 100);
1247  } else {
1248  $stripefee = round($fee);
1249  }
1250 
1251  $paymentarray = array(
1252  "amount" => "$stripeamount",
1253  "currency" => "$currency",
1254  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1255  "description" => "Stripe payment: ".$description,
1256  "capture" => $capture,
1257  "metadata" => $metadata,
1258  "source" => "$source",
1259  "customer" => "$customer"
1260  );
1261  if ($conf->entity != $conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0) {
1262  $paymentarray["application_fee_amount"] = $stripefee;
1263  }
1264  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1265  $paymentarray["receipt_email"] = $societe->email;
1266  }
1267 
1268  if (preg_match('/pm_/i', $source)) {
1269  $stripecard = $source;
1270  $amountstripe = $stripeamount;
1271  $FULLTAG = 'PFBO'; // Payment From Back Office
1272  $stripe = $return;
1273  $amounttopay = $amount;
1274  $servicestatus = $status;
1275 
1276  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1277  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1278 
1279  dol_syslog("* createPaymentStripe Create payment on card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1280 
1281  // Create payment intent and charge payment (confirmnow = true)
1282  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1283 
1284  $charge = new stdClass();
1285  if ($paymentintent->status == 'succeeded') {
1286  $charge->status = 'ok';
1287  $charge->id = $paymentintent->id;
1288  } else {
1289  $charge->status = 'failed';
1290  $charge->failure_code = $stripe->code;
1291  $charge->failure_message = $stripe->error;
1292  $charge->failure_declinecode = $stripe->declinecode;
1293  }
1294  } else {
1295  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
1296  }
1297  }
1298  if (isset($charge->id)) {
1299  }
1300 
1301  $return->statut = 'success';
1302  $return->id = $charge->id;
1303 
1304  if (preg_match('/pm_/i', $source)) {
1305  $return->message = 'Payment retrieved by card status = '.$charge->status;
1306  } else {
1307  if ($charge->source->type == 'card') {
1308  $return->message = $charge->source->card->brand." ....".$charge->source->card->last4;
1309  } elseif ($charge->source->type == 'three_d_secure') {
1310  $stripe = new Stripe($this->db);
1311  $src = \Stripe\Source::retrieve("".$charge->source->three_d_secure->card, array(
1312  "stripe_account" => $stripe->getStripeAccount($service)
1313  ));
1314  $return->message = $src->card->brand." ....".$src->card->last4;
1315  } else {
1316  $return->message = $charge->id;
1317  }
1318  }
1319  } catch (\Stripe\Error\Card $e) {
1320  include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
1321  // Since it's a decline, \Stripe\Error\Card will be caught
1322  $body = $e->getJsonBody();
1323  $err = $body['error'];
1324 
1325  $return->statut = 'error';
1326  $return->id = $err['charge'];
1327  $return->type = $err['type'];
1328  $return->code = $err['code'];
1329  $return->message = $err['message'];
1330  $body = "Error: <br>".$return->id." ".$return->message." ";
1331  $subject = '[Alert] Payment error using Stripe';
1332  $cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
1333  $cmailfile->sendfile();
1334 
1335  $error++;
1336  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1337  } catch (\Stripe\Error\RateLimit $e) {
1338  // Too many requests made to the API too quickly
1339  $error++;
1340  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1341  } catch (\Stripe\Error\InvalidRequest $e) {
1342  // Invalid parameters were supplied to Stripe's API
1343  $error++;
1344  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1345  } catch (\Stripe\Error\Authentication $e) {
1346  // Authentication with Stripe's API failed
1347  // (maybe you changed API keys recently)
1348  $error++;
1349  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1350  } catch (\Stripe\Error\ApiConnection $e) {
1351  // Network communication with Stripe failed
1352  $error++;
1353  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1354  } catch (\Stripe\Error\Base $e) {
1355  // Display a very generic error to the user, and maybe send
1356  // yourself an email
1357  $error++;
1358  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1359  } catch (Exception $e) {
1360  // Something else happened, completely unrelated to Stripe
1361  $error++;
1362  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1363  }
1364  return $return;
1365  }
1366 }
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:156
Societe
Class to manage third parties objects (customers, suppliers, prospects...)
Definition: societe.class.php:50
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:4006
Stripe\sepaStripe
sepaStripe($cu, CompanyPaymentMode $object, $stripeacc='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe SEPA of a company payment mode.
Definition: stripe.class.php:945
Stripe\__construct
__construct($db)
Constructor.
Definition: stripe.class.php:75
$sql
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)) $sql
Social contributions to pay.
Definition: index.php:745
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:567
Stripe\createPaymentStripe
createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status=0, $usethirdpartyemailforreceiptemail=0, $capture=true)
Create charge.
Definition: stripe.class.php:1105
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:5052
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:640
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:347
Stripe\getSelectedReader
getSelectedReader($reader, $key='', $status=0)
Get the Stripe reader Object from its ID.
Definition: stripe.class.php:301
Stripe\getStripeCustomerAccount
getStripeCustomerAccount($id, $status=0, $site_account='')
getStripeCustomerAccount
Definition: stripe.class.php:138
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:4082
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:46
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1689
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:793
getDolGlobalString
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
Definition: functions.lib.php:102
Stripe\getPaymentMethodStripe
getPaymentMethodStripe($paymentmethod, $key='', $status=0)
Get the Stripe payment method Object from its ID.
Definition: stripe.class.php:273
dol_now
dol_now($mode='auto')
Return date for now.
Definition: functions.lib.php:3003
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:116
getUserRemoteIP
getUserRemoteIP()
Return the IP of remote user.
Definition: functions.lib.php:3689
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