dolibarr  17.0.4
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(''.$paymentmethod->id.'');
283  } else {
284  $stripepaymentmethod = \Stripe\PaymentMethod::retrieve(''.$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(''.$reader.'');
311  } else {
312  $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve(''.$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  //"save_payment_method" => true,
469  "setup_future_usage" => "on_session",
470  "metadata" => $metadata
471  );
472  if ($descriptor) {
473  $dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
474  $dataforintent["statement_descriptor"] = $descriptor; // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
475  }
476  if (!is_null($customer)) {
477  $dataforintent["customer"] = $customer;
478  }
479  // payment_method =
480  // payment_method_types = array('card')
481  //var_dump($dataforintent);
482  if ($off_session) {
483  unset($dataforintent['setup_future_usage']);
484  // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
485  // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
486  //$dataforintent["setup_future_usage"] = "off_session";
487  $dataforintent["off_session"] = true;
488  }
489  if (getDolGlobalInt('STRIPE_GIROPAY')) {
490  unset($dataforintent['setup_future_usage']);
491  }
492  if (getDolGlobalInt('STRIPE_KLARNA')) {
493  unset($dataforintent['setup_future_usage']);
494  }
495  if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
496  unset($dataforintent['setup_future_usage']);
497  $dataforintent["capture_method"] = "manual";
498  $dataforintent["confirmation_method"] = "manual";
499  }
500  if (!is_null($payment_method)) {
501  $dataforintent["payment_method"] = $payment_method;
502  $description .= ' - '.$payment_method;
503  }
504 
505  if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
506  $dataforintent["application_fee_amount"] = $stripefee;
507  }
508  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
509  $dataforintent["receipt_email"] = $object->thirdparty->email;
510  }
511 
512  try {
513  // Force to use the correct API key
514  global $stripearrayofkeysbyenv;
515  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
516 
517  $arrayofoptions = array();
518  if (empty($noidempotency_key)) {
519  $arrayofoptions["idempotency_key"] = $description;
520  }
521  // 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.
522  if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
523  $arrayofoptions["stripe_account"] = $key;
524  }
525 
526  dol_syslog("dataforintent to create paymentintent = ".var_export($dataforintent, true));
527 
528  $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
529 
530  // Store the payment intent
531  if (is_object($object)) {
532  $paymentintentalreadyexists = 0;
533  // Check that payment intent $paymentintent->id is not already recorded.
534  $sql = "SELECT pi.rowid";
535  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
536  $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
537  $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
538  $sql .= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
539 
540  dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
541  $resql = $this->db->query($sql);
542  if ($resql) {
543  $num = $this->db->num_rows($resql);
544  if ($num) {
545  $obj = $this->db->fetch_object($resql);
546  if ($obj) {
547  $paymentintentalreadyexists++;
548  }
549  }
550  } else {
551  dol_print_error($this->db);
552  }
553 
554  // If not, we create it.
555  if (!$paymentintentalreadyexists) {
556  $now = dol_now();
557  $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
558  $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).")";
559  $resql = $this->db->query($sql);
560  if (!$resql) {
561  $error++;
562  $this->error = $this->db->lasterror();
563  dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
564  }
565  }
566  } else {
567  $_SESSION["stripe_payment_intent"] = $paymentintent;
568  }
569  } catch (Stripe\Error\Card $e) {
570  $error++;
571  $this->error = $e->getMessage();
572  $this->code = $e->getStripeCode();
573  $this->declinecode = $e->getDeclineCode();
574  } catch (Exception $e) {
575  //var_dump($dataforintent);
576  //var_dump($description);
577  //var_dump($key);
578  //var_dump($paymentintent);
579  //var_dump($e->getMessage());
580  //var_dump($e);
581  $error++;
582  $this->error = $e->getMessage();
583  $this->code = '';
584  $this->declinecode = '';
585  }
586  }
587 
588  dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
589 
590  if (!$error) {
591  return $paymentintent;
592  } else {
593  return null;
594  }
595  }
596 
615  public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
616  {
617  global $conf;
618 
619  dol_syslog("getSetupIntent description=".$description.' confirmnow='.$confirmnow, LOG_INFO, 1);
620 
621  $error = 0;
622 
623  if (empty($status)) {
624  $service = 'StripeTest';
625  } else {
626  $service = 'StripeLive';
627  }
628 
629  $setupintent = null;
630 
631  if (empty($setupintent)) {
632  $ipaddress = getUserRemoteIP();
633  $metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
634  if (is_object($object)) {
635  $metadata['dol_type'] = $object->element;
636  $metadata['dol_id'] = $object->id;
637  if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
638  $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
639  }
640  }
641 
642  // list of payment method types
643  $paymentmethodtypes = array("card");
644  if (!empty($conf->global->STRIPE_SEPA_DIRECT_DEBIT)) {
645  $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
646  }
647  if (!empty($conf->global->STRIPE_BANCONTACT)) {
648  $paymentmethodtypes[] = "bancontact";
649  }
650  if (!empty($conf->global->STRIPE_IDEAL)) {
651  $paymentmethodtypes[] = "ideal";
652  }
653  // Giropay not possible for setup intent
654  if (!empty($conf->global->STRIPE_SOFORT)) {
655  $paymentmethodtypes[] = "sofort";
656  }
657 
658  $dataforintent = array(
659  "confirm" => $confirmnow, // Do not confirm immediatly during creation of intent
660  "payment_method_types" => $paymentmethodtypes,
661  "usage" => "off_session",
662  "metadata" => $metadata
663  );
664  if (!is_null($customer)) {
665  $dataforintent["customer"] = $customer;
666  }
667  if (!is_null($description)) {
668  $dataforintent["description"] = $description;
669  }
670  // payment_method =
671  // payment_method_types = array('card')
672  //var_dump($dataforintent);
673 
674  if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
675  $dataforintent["receipt_email"] = $object->thirdparty->email;
676  }
677 
678  try {
679  // Force to use the correct API key
680  global $stripearrayofkeysbyenv;
681  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
682 
683  dol_syslog("getSetupIntent ".$stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG);
684 
685  // 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.
686  if (empty($key)) { // If the Stripe connect account not set, we use common API usage
687  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
688  $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
689  } else {
690  //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
691  $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
692  }
693  //var_dump($setupintent->id);
694 
695  // Store the setup intent
696  /*if (is_object($object))
697  {
698  $setupintentalreadyexists = 0;
699  // Check that payment intent $setupintent->id is not already recorded.
700  $sql = "SELECT pi.rowid";
701  $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
702  $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
703  $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
704  $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
705 
706  dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
707  $resql = $this->db->query($sql);
708  if ($resql) {
709  $num = $this->db->num_rows($resql);
710  if ($num)
711  {
712  $obj = $this->db->fetch_object($resql);
713  if ($obj) $setupintentalreadyexists++;
714  }
715  }
716  else dol_print_error($this->db);
717 
718  // If not, we create it.
719  if (! $setupintentalreadyexists)
720  {
721  $now=dol_now();
722  $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
723  $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).")";
724  $resql = $this->db->query($sql);
725  if (! $resql)
726  {
727  $error++;
728  $this->error = $this->db->lasterror();
729  dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
730  }
731  }
732  }
733  else
734  {
735  $_SESSION["stripe_setup_intent"] = $setupintent;
736  }*/
737  } catch (Exception $e) {
738  //var_dump($dataforintent);
739  //var_dump($description);
740  //var_dump($key);
741  //var_dump($setupintent);
742  //var_dump($e->getMessage());
743  $error++;
744  $this->error = $e->getMessage();
745  }
746  }
747 
748  if (!$error) {
749  dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
750  return $setupintent;
751  } else {
752  dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
753  return null;
754  }
755  }
756 
757 
768  public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
769  {
770  global $conf, $user, $langs;
771 
772  $card = null;
773 
774  $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_....
775  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
776  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
777  $sql .= " AND sa.type = 'card'";
778 
779  dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
780  $resql = $this->db->query($sql);
781  if ($resql) {
782  $num = $this->db->num_rows($resql);
783  if ($num) {
784  $obj = $this->db->fetch_object($resql);
785  $cardref = $obj->stripe_card_ref;
786  dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
787  if ($cardref) {
788  try {
789  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
790  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
791  $card = $cu->sources->retrieve($cardref);
792  } else {
793  $card = \Stripe\PaymentMethod::retrieve($cardref);
794  }
795  } else {
796  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
797  //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
798  $card = $cu->sources->retrieve($cardref);
799  } else {
800  //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
801  $card = \Stripe\PaymentMethod::retrieve($cardref);
802  }
803  }
804  } catch (Exception $e) {
805  $this->error = $e->getMessage();
806  dol_syslog($this->error, LOG_WARNING);
807  }
808  } elseif ($createifnotlinkedtostripe) {
809  $exp_date_month = $obj->exp_date_month;
810  $exp_date_year = $obj->exp_date_year;
811  $number = $obj->number;
812  $cvc = $obj->cvn; // cvn in database, cvc for stripe
813  $cardholdername = $obj->proprio;
814 
815  $ipaddress = getUserRemoteIP();
816 
817  $dataforcard = array(
818  "source" => array('object'=>'card', 'exp_month'=>$exp_date_month, 'exp_year'=>$exp_date_year, 'number'=>$number, 'cvc'=>$cvc, 'name'=>$cardholdername),
819  "metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
820  );
821 
822  //$a = \Stripe\Stripe::getApiKey();
823  //var_dump($a);
824  //var_dump($stripeacc);exit;
825  try {
826  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
827  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
828  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
829  $card = $cu->sources->create($dataforcard);
830  if (!$card) {
831  $this->error = 'Creation of card on Stripe has failed';
832  }
833  } else {
834  $connect = '';
835  if (!empty($stripeacc)) {
836  $connect = $stripeacc.'/';
837  }
838  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
839  if ($status) {
840  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
841  }
842  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
843 
844  //dol_syslog("Error: This case is not supported", LOG_ERR);
845  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
846  }
847  } else {
848  if (empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
849  dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
850  $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
851  if (!$card) {
852  $this->error = 'Creation of card on Stripe has failed';
853  }
854  } else {
855  $connect = '';
856  if (!empty($stripeacc)) {
857  $connect = $stripeacc.'/';
858  }
859  $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
860  if ($status) {
861  $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
862  }
863  $urtoswitchonstripe = ' <a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
864 
865  //dol_syslog("Error: This case is not supported", LOG_ERR);
866  $this->error = $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', $urtoswitchonstripe);
867  }
868  }
869 
870  if ($card) {
871  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
872  $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
873  $sql .= " country_code = '".$this->db->escape($card->country)."',";
874  $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
875  $sql .= " WHERE rowid = ".((int) $object->id);
876  $sql .= " AND type = 'card'";
877  $resql = $this->db->query($sql);
878  if (!$resql) {
879  $this->error = $this->db->lasterror();
880  }
881  }
882  } catch (Exception $e) {
883  $this->error = $e->getMessage();
884  dol_syslog($this->error, LOG_WARNING);
885  }
886  }
887  }
888  } else {
889  dol_print_error($this->db);
890  }
891 
892  return $card;
893  }
894 
895 
906  public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
907  {
908  global $conf, $user, $langs;
909  $sepa = null;
910 
911  $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix"; // stripe_card_ref is src_ for sepa
912  $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
913  $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
914  $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
915 
916  $soc = new Societe($this->db);
917  $soc->fetch($object->fk_soc);
918 
919  dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
920  $resql = $this->db->query($sql);
921  if ($resql) {
922  $num = $this->db->num_rows($resql);
923  if ($num) {
924  $obj = $this->db->fetch_object($resql);
925  $cardref = $obj->stripe_card_ref;
926  dol_syslog(get_class($this)."::sepaStripe cardref=".$cardref);
927  if ($cardref) {
928  try {
929  if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
930  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
931  $sepa = $cu->sources->retrieve($cardref);
932  } else {
933  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
934  }
935  } else {
936  if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
937  //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
938  $sepa = $cu->sources->retrieve($cardref);
939  } else {
940  //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
941  $sepa = \Stripe\PaymentMethod::retrieve($cardref);
942  }
943  }
944  } catch (Exception $e) {
945  $this->error = $e->getMessage();
946  dol_syslog($this->error, LOG_WARNING);
947  }
948  } elseif ($createifnotlinkedtostripe) {
949  $iban = $obj->iban_prefix; //prefix ?
950  $ipaddress = getUserRemoteIP();
951 
952  $dataforcard = array(
953  'type'=>'sepa_debit',
954  "sepa_debit" => array('iban' => $iban),
955  'currency' => 'eur',
956  'usage' => 'reusable',
957  'owner' => array(
958  'name' => $soc->name,
959  ),
960  "metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
961  );
962 
963  //$a = \Stripe\Stripe::getApiKey();
964  //var_dump($a);var_dump($stripeacc);exit;
965  try {
966  dol_syslog("Try to create sepa_debit 0");
967 
968  $service = 'StripeTest';
969  $servicestatus = 0;
970  if (!empty($conf->global->STRIPE_LIVE) && !GETPOST('forcesandbox', 'alpha')) {
971  $service = 'StripeLive';
972  $servicestatus = 1;
973  }
974  // Force to use the correct API key
975  global $stripearrayofkeysbyenv;
976  $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
977 
978  dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
979  $s = new \Stripe\StripeClient($stripeacc);
980  $sepa = $s->sources->create($dataforcard);
981  if (!$sepa) {
982  $this->error = 'Creation of sepa_debit on Stripe has failed';
983  } else {
984  // association du client avec cette source de paimeent
985  $cs = $cu->createSource($cu->id, array('source' => $sepa->id));
986  if (!$cs) {
987  $this->error = 'Link SEPA <-> Customer failed';
988  } else {
989  dol_syslog("Try to create sepa_debit 3");
990  // print json_encode($sepa);
991 
992  $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
993  $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."', card_type = 'sepa_debit',";
994  $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "'";
995  $sql .= " WHERE rowid = ".((int) $object->id);
996  $sql .= " AND type = 'ban'";
997  $resql = $this->db->query($sql);
998  if (!$resql) {
999  $this->error = $this->db->lasterror();
1000  }
1001  }
1002  }
1003  } catch (Exception $e) {
1004  $this->error = $e->getMessage();
1005  dol_syslog($this->error, LOG_WARNING);
1006  }
1007  }
1008  }
1009  } else {
1010  dol_print_error($this->db);
1011  }
1012 
1013  return $sepa;
1014  }
1015 
1032  public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
1033  {
1034  global $conf;
1035 
1036  $error = 0;
1037 
1038  if (empty($status)) {
1039  $service = 'StripeTest';
1040  } else {
1041  $service = 'StripeLive';
1042  }
1043 
1044  $sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
1045  $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
1046  $sql .= " WHERE sa.key_account = '".$this->db->escape($customer)."'";
1047  //$sql.= " AND sa.entity IN (".getEntity('societe').")";
1048  $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
1049 
1050  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1051  $result = $this->db->query($sql);
1052  if ($result) {
1053  if ($this->db->num_rows($result)) {
1054  $obj = $this->db->fetch_object($result);
1055  $key = $obj->fk_soc;
1056  } else {
1057  $key = null;
1058  }
1059  } else {
1060  $key = null;
1061  }
1062 
1063  $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
1064  if (!in_array($currency, $arrayzerounitcurrency)) {
1065  $stripeamount = $amount * 100;
1066  } else {
1067  $stripeamount = $amount;
1068  }
1069 
1070  $societe = new Societe($this->db);
1071  if ($key > 0) {
1072  $societe->fetch($key);
1073  }
1074 
1075  $description = "";
1076  $ref = "";
1077  if ($origin == 'order') {
1078  $order = new Commande($this->db);
1079  $order->fetch($item);
1080  $ref = $order->ref;
1081  $description = "ORD=".$ref.".CUS=".$societe->id.".PM=stripe";
1082  } elseif ($origin == 'invoice') {
1083  $invoice = new Facture($this->db);
1084  $invoice->fetch($item);
1085  $ref = $invoice->ref;
1086  $description = "INV=".$ref.".CUS=".$societe->id.".PM=stripe";
1087  }
1088 
1089  $ipaddress = getUserRemoteIP();
1090 
1091  $metadata = array(
1092  "dol_id" => "".$item."",
1093  "dol_type" => "".$origin."",
1094  "dol_thirdparty_id" => "".$societe->id."",
1095  'dol_thirdparty_name' => $societe->name,
1096  'dol_version'=>DOL_VERSION,
1097  'dol_entity'=>$conf->entity,
1098  'ipaddress'=>$ipaddress
1099  );
1100  $return = new Stripe($this->db);
1101  try {
1102  // Force to use the correct API key
1103  global $stripearrayofkeysbyenv;
1104  \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
1105 
1106  if (empty($conf->stripeconnect->enabled)) { // With a common Stripe account
1107  if (preg_match('/pm_/i', $source)) {
1108  $stripecard = $source;
1109  $amountstripe = $stripeamount;
1110  $FULLTAG = 'PFBO'; // Payment From Back Office
1111  $stripe = $return;
1112  $amounttopay = $amount;
1113  $servicestatus = $status;
1114 
1115  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1116  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1117 
1118  dol_syslog("* createPaymentStripe Create payment for customer ".$customer->id." on source card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1119 
1120  // Create payment intent and charge payment (confirmnow = true)
1121  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1122 
1123  $charge = new stdClass();
1124  if ($paymentintent->status == 'succeeded') {
1125  $charge->status = 'ok';
1126  } else {
1127  $charge->status = 'failed';
1128  $charge->failure_code = $stripe->code;
1129  $charge->failure_message = $stripe->error;
1130  $charge->failure_declinecode = $stripe->declinecode;
1131  $stripefailurecode = $stripe->code;
1132  $stripefailuremessage = $stripe->error;
1133  $stripefailuredeclinecode = $stripe->declinecode;
1134  }
1135  } elseif (preg_match('/acct_/i', $source)) {
1136  $charge = \Stripe\Charge::create(array(
1137  "amount" => "$stripeamount",
1138  "currency" => "$currency",
1139  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1140  "description" => "Stripe payment: ".$description,
1141  "capture" => $capture,
1142  "metadata" => $metadata,
1143  "source" => "$source"
1144  ));
1145  } else {
1146  $paymentarray = array(
1147  "amount" => "$stripeamount",
1148  "currency" => "$currency",
1149  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1150  "description" => "Stripe payment: ".$description,
1151  "capture" => $capture,
1152  "metadata" => $metadata,
1153  "source" => "$source",
1154  "customer" => "$customer"
1155  );
1156 
1157  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1158  $paymentarray["receipt_email"] = $societe->email;
1159  }
1160 
1161  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
1162  }
1163  } else {
1164  // With Stripe Connect
1165  $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
1166  if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1167  $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
1168  } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1169  $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
1170  }
1171 
1172  if (!in_array($currency, $arrayzerounitcurrency)) {
1173  $stripefee = round($fee * 100);
1174  } else {
1175  $stripefee = round($fee);
1176  }
1177 
1178  $paymentarray = array(
1179  "amount" => "$stripeamount",
1180  "currency" => "$currency",
1181  "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1182  "description" => "Stripe payment: ".$description,
1183  "capture" => $capture,
1184  "metadata" => $metadata,
1185  "source" => "$source",
1186  "customer" => "$customer"
1187  );
1188  if ($conf->entity != $conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0) {
1189  $paymentarray["application_fee_amount"] = $stripefee;
1190  }
1191  if ($societe->email && $usethirdpartyemailforreceiptemail) {
1192  $paymentarray["receipt_email"] = $societe->email;
1193  }
1194 
1195  if (preg_match('/pm_/i', $source)) {
1196  $stripecard = $source;
1197  $amountstripe = $stripeamount;
1198  $FULLTAG = 'PFBO'; // Payment From Back Office
1199  $stripe = $return;
1200  $amounttopay = $amount;
1201  $servicestatus = $status;
1202 
1203  dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1204  $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1205 
1206  dol_syslog("* createPaymentStripe Create payment on card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1207 
1208  // Create payment intent and charge payment (confirmnow = true)
1209  $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1210 
1211  $charge = new stdClass();
1212  if ($paymentintent->status == 'succeeded') {
1213  $charge->status = 'ok';
1214  $charge->id = $paymentintent->id;
1215  } else {
1216  $charge->status = 'failed';
1217  $charge->failure_code = $stripe->code;
1218  $charge->failure_message = $stripe->error;
1219  $charge->failure_declinecode = $stripe->declinecode;
1220  }
1221  } else {
1222  $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
1223  }
1224  }
1225  if (isset($charge->id)) {
1226  }
1227 
1228  $return->statut = 'success';
1229  $return->id = $charge->id;
1230 
1231  if (preg_match('/pm_/i', $source)) {
1232  $return->message = 'Payment retrieved by card status = '.$charge->status;
1233  } else {
1234  if ($charge->source->type == 'card') {
1235  $return->message = $charge->source->card->brand." ....".$charge->source->card->last4;
1236  } elseif ($charge->source->type == 'three_d_secure') {
1237  $stripe = new Stripe($this->db);
1238  $src = \Stripe\Source::retrieve("".$charge->source->three_d_secure->card."", array(
1239  "stripe_account" => $stripe->getStripeAccount($service)
1240  ));
1241  $return->message = $src->card->brand." ....".$src->card->last4;
1242  } else {
1243  $return->message = $charge->id;
1244  }
1245  }
1246  } catch (\Stripe\Error\Card $e) {
1247  include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
1248  // Since it's a decline, \Stripe\Error\Card will be caught
1249  $body = $e->getJsonBody();
1250  $err = $body['error'];
1251 
1252  $return->statut = 'error';
1253  $return->id = $err['charge'];
1254  $return->type = $err['type'];
1255  $return->code = $err['code'];
1256  $return->message = $err['message'];
1257  $body = "Error: <br>".$return->id." ".$return->message." ";
1258  $subject = '[Alert] Payment error using Stripe';
1259  $cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
1260  $cmailfile->sendfile();
1261 
1262  $error++;
1263  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1264  } catch (\Stripe\Error\RateLimit $e) {
1265  // Too many requests made to the API too quickly
1266  $error++;
1267  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1268  } catch (\Stripe\Error\InvalidRequest $e) {
1269  // Invalid parameters were supplied to Stripe's API
1270  $error++;
1271  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1272  } catch (\Stripe\Error\Authentication $e) {
1273  // Authentication with Stripe's API failed
1274  // (maybe you changed API keys recently)
1275  $error++;
1276  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1277  } catch (\Stripe\Error\ApiConnection $e) {
1278  // Network communication with Stripe failed
1279  $error++;
1280  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1281  } catch (\Stripe\Error\Base $e) {
1282  // Display a very generic error to the user, and maybe send
1283  // yourself an email
1284  $error++;
1285  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1286  } catch (Exception $e) {
1287  // Something else happened, completely unrelated to Stripe
1288  $error++;
1289  dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1290  }
1291  return $return;
1292  }
1293 }
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,...