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