dolibarr 24.0.0-beta
stripe.class.php
1<?php
2/* Copyright (C) 2018-2021 Thibault FOUCART <support@ptibogxiv.net>
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20// Put here all includes required by your class file
21require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
22require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
23require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
24require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
25require_once DOL_DOCUMENT_ROOT.'/stripe/config.php'; // This set stripe global $stripearrayofkeys and $stripearrayofkeysbyenv
26
31class Stripe extends CommonObject
32{
36 public $rowid;
37
41 public $fk_soc;
42
46 public $fk_key;
47
51 public $id; // @phpstan-ignore-line
52
56 public $mode;
57
61 public $entity;
62
66 public $type;
67
71 public $code;
72
76 public $declinecode;
77
81 public $message;
82
88 public function __construct($db)
89 {
90 $this->db = $db;
91 }
92
93
102 public function getStripeAccount($mode = 'StripeTest', $fk_soc = 0, $entity = -1)
103 {
104 global $conf;
105
106 $key = '';
107 if ($entity < 0) {
108 $entity = $conf->entity;
109 }
110
111 $sql = "SELECT tokenstring";
112 $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
113 $sql .= " WHERE service = '".$this->db->escape($mode)."'";
114 $sql .= " AND entity = ".((int) $entity);
115 if ($fk_soc > 0) {
116 $sql .= " AND fk_soc = ".((int) $fk_soc);
117 } else {
118 $sql .= " AND fk_soc IS NULL";
119 }
120 $sql .= " AND fk_user IS NULL AND fk_adherent IS NULL";
121
122 dol_syslog(get_class($this)."::getStripeAccount", LOG_DEBUG);
123
124 $result = $this->db->query($sql);
125 if ($result) {
126 if ($this->db->num_rows($result)) {
127 $obj = $this->db->fetch_object($result);
128 $tokenstring = $obj->tokenstring;
129
130 if ($tokenstring) {
131 $tmparray = json_decode($tokenstring);
132 $key = empty($tmparray->stripe_user_id) ? '' : $tmparray->stripe_user_id;
133 }
134 } else {
135 $tokenstring = '';
136 }
137 } else {
138 dol_print_error($this->db);
139 }
140
141 dol_syslog("No dedicated Stripe Connect account available for entity ".$conf->entity);
142
143 return $key;
144 }
145
154 public function getStripeCustomerAccount($id, $status = 0, $site_account = '')
155 {
156 include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
157 $societeaccount = new SocieteAccount($this->db);
158 return $societeaccount->getCustomerAccount($id, 'stripe', $status, $site_account); // Get thirdparty cus_...
159 }
160
161
172 public function customerStripe($object, $key = '', $status = 0, $createifnotlinkedtostripe = 0)
173 {
174 global $conf, $user;
175
176 if (empty($object->id)) {
177 dol_syslog("customerStripe is called with the parameter object that is not loaded");
178 return null;
179 }
180
181 $customer = null;
182
183 // Force to use the correct API key
184 global $stripearrayofkeysbyenv;
185 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
186
187 $sql = "SELECT sa.key_account as key_account, sa.entity"; // key_account is cus_....
188 $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
189 $sql .= " WHERE sa.fk_soc = ".((int) $object->id);
190 $sql .= " AND sa.entity IN (".getEntity('societe').")";
191 $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
192 $sql .= " AND (sa.site_account IS NULL OR sa.site_account = '' OR sa.site_account = '".$this->db->escape($stripearrayofkeysbyenv[$status]['publishable_key'])."')";
193 $sql .= " AND sa.key_account IS NOT NULL AND sa.key_account <> ''";
194 $sql .= " ORDER BY sa.site_account DESC, sa.rowid DESC"; // To get the entry with a site_account defined in priority
195
196 dol_syslog(get_class($this)."::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG);
197 $resql = $this->db->query($sql);
198 if ($resql) {
199 $num = $this->db->num_rows($resql);
200 if ($num) {
201 $obj = $this->db->fetch_object($resql);
202 $tiers = $obj->key_account;
203
204 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']);
205
206 try {
207 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
208 //$customer = \Stripe\Customer::retrieve("$tiers");
209 $customer = \Stripe\Customer::retrieve(array('id' => "$tiers", 'expand[]' => 'sources'));
210 } else {
211 //$customer = \Stripe\Customer::retrieve("$tiers", array("stripe_account" => $key));
212 $customer = \Stripe\Customer::retrieve(array('id' => "$tiers", 'expand[]' => 'sources'), array("stripe_account" => $key));
213 }
214 } catch (Exception $e) {
215 // For example, 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.'
216 $this->error = $e->getMessage();
217 }
218 } elseif ($createifnotlinkedtostripe) {
219 $ipaddress = getUserRemoteIP();
220
221 $dataforcustomer = array(
222 "email" => $object->email,
223 "description" => $object->name,
224 "metadata" => array('dol_id' => $object->id, 'dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress)
225 );
226
227 $vatcleaned = $object->tva_intra ? $object->tva_intra : null;
228
229 /*
230 $taxinfo = array('type'=>'vat');
231 if ($vatcleaned)
232 {
233 $taxinfo["tax_id"] = $vatcleaned;
234 }
235 // We force data to "null" if not defined as expected by Stripe
236 if (empty($vatcleaned)) $taxinfo=null;
237 $dataforcustomer["tax_info"] = $taxinfo;
238 */
239
240 //$a = \Stripe\Stripe::getApiKey();
241 //var_dump($a);var_dump($key);exit;
242 try {
243 // Force to use the correct API key
244 global $stripearrayofkeysbyenv;
245 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
246
247 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
248 $customer = \Stripe\Customer::create($dataforcustomer);
249 } else {
250 $customer = \Stripe\Customer::create($dataforcustomer, array("stripe_account" => $key));
251 }
252
253 // Create the VAT record in Stripe
254 if (getDolGlobalString('STRIPE_SAVE_TAX_IDS')) { // We setup to save Tax info on Stripe side. Warning: This may result in error when saving customer
255 if (!empty($vatcleaned)) {
256 $isineec = isInEEC($object);
257 if ($object->country_code && $isineec) {
258 //$taxids = $customer->allTaxIds($customer);
259 $customer->createTaxId($customer->id, array('type' => 'eu_vat', 'value' => $vatcleaned));
260 }
261 }
262 }
263
264 // Create customer in Dolibarr
265 $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_account (fk_soc, login, key_account, site, site_account, status, entity, date_creation, fk_user_creat)";
266 $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).")";
267 $resql = $this->db->query($sql);
268 if (!$resql) {
269 $this->error = $this->db->lasterror();
270 }
271 } catch (Exception $e) {
272 $this->error = $e->getMessage();
273 }
274 }
275 } else {
276 dol_print_error($this->db);
277 }
278
279 return $customer;
280 }
281
290 public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0)
291 {
292 $stripepaymentmethod = null;
293
294 try {
295 // Force to use the correct API key
296 global $stripearrayofkeysbyenv;
297 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
298 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
299 $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id);
300 } else {
301 $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id, array("stripe_account" => $key));
302 }
303 } catch (Exception $e) {
304 $this->error = $e->getMessage();
305 }
306
307 return $stripepaymentmethod;
308 }
309
318 public function getSelectedReader($reader, $key = '', $status = 0)
319 {
320 $selectedreader = null;
321
322 try {
323 // Force to use the correct API key
324 global $stripearrayofkeysbyenv;
325 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
326 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
327 $selectedreader = \Stripe\Terminal\Reader::retrieve((string) $reader);
328 } else {
329 $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve((string) $reader, array("stripe_account" => $key));
330 }
331 } catch (Exception $e) {
332 $this->error = $e->getMessage();
333 }
334
335 return $selectedreader;
336 }
337
338
347 public function convertAmount($amount, $currency_code, $direction = 0)
348 {
349 $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
350 if (!in_array($currency_code, $arrayzerounitcurrency)) {
351 if (empty($direction)) {
352 $newamount = (int) round($amount * 100); // If $amount is 79.99, doing 79.99 * 100 returns float 7998.999999999999, and "int" do a truncation into 7998 so we must first use round to get nearest integer value
353 } else {
354 $newamount = (float) ($amount / 100);
355 }
356 } else {
357 $newamount = $amount;
358 }
359
360 return $newamount;
361 }
362
363
390 public function getPaymentIntent($amount, $currency_code, $tag, $description = '', $object = null, $customer = null, $key = null, $servicestatus = 0, $usethirdpartyemailforreceiptemail = 0, $mode = 'automatic', $confirmnow = false, $payment_method = null, $off_session = 0, $noidempotency_key = 1, $did = 0)
391 {
392 global $conf, $user, $hookmanager;
393
394 dol_syslog(get_class($this)."::getPaymentIntent description=".$description, LOG_INFO, 1);
395
396 $error = 0;
397
398 if (empty($servicestatus)) {
399 $service = 'StripeTest';
400 } else {
401 $service = 'StripeLive';
402 }
403
404 $stripeamount = $this->convertAmount($amount, $currency_code, 0);
405
406 $fee = 0;
407 if (getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT")) {
408 $fee = $amount * ((float) getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT", '0') / 100) + (float) getDolGlobalString("STRIPE_APPLICATION_FEE", '0');
409 }
410 if ($fee >= (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') && (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') > (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
411 $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0');
412 } elseif ($fee < (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
413 $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0');
414 }
415 $stripefee = round($this->convertAmount($fee, $currency_code));
416
417 $paymentintent = null;
418
419 if (is_object($object) && getDolGlobalInt('STRIPE_REUSE_EXISTING_INTENT_IF_FOUND') && !getDolGlobalInt('STRIPE_CARD_PRESENT')) {
420 // Warning. If a payment was tried and failed, a payment intent was created.
421 // 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.
422 // 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
423 // automatically return the existing payment intent if idempotency is provided when we try to create the new one.
424 // 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)
425
426 // Try to retrieve the last paymentintent for invoice, but if it fails, never mind.
427 $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
428 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
429 $sql .= " WHERE pi.fk_facture = ".((int) $object->id);
430 $sql .= " AND pi.sourcetype = '".$this->db->escape($object->element)."'";
431 $sql .= " AND pi.entity IN (".getEntity('societe').")";
432 $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
433 $sql .= " ORDER BY rowid DESC";
434
435 dol_syslog(get_class($this)."::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
436
437 $resql = $this->db->query($sql);
438 if ($resql) {
439 $num = $this->db->num_rows($resql);
440 if ($num) {
441 $obj = $this->db->fetch_object($resql);
442
443 $ext_payment_intent = $obj->ext_payment_id;
444 $ext_payment_intent_array = preg_split('/[:@]/', $ext_payment_intent);
445
446 $intent = $ext_payment_intent_array[0];
447 $customerindb = (isset($ext_payment_intent_array[1]) ? $ext_payment_intent_array[1] : '');
448 $pkeyindb = (isset($ext_payment_intent_array[2]) ? $ext_payment_intent_array[2] : '');
449 // TODO Test that $pkeyindb and $customerindb match
450
451 dol_syslog(get_class($this)."::getPaymentIntent found existing payment intent record with intent=".$intent);
452
453 // Force to use the correct API key
454 global $stripearrayofkeysbyenv;
455 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$servicestatus]['secret_key']);
456
457 try {
458 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
459 $paymentintent = \Stripe\PaymentIntent::retrieve($intent);
460 } else {
461 $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
462 }
463 } catch (Exception $e) {
464 $error++;
465 $this->error = $e->getMessage();
466 }
467 }
468 }
469 }
470
471 if (empty($paymentintent)) {
472 // Try to create intent. See https://stripe.com/docs/api/payment_intents/create
473 $ipaddress = getUserRemoteIP();
474 $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress, 'dol_noidempotency' => (int) $noidempotency_key);
475 if (is_object($object)) {
476 $metadata['dol_type'] = $object->element;
477 $metadata['dol_id'] = $object->id;
478 if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
479 $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
480 }
481 }
482
483 $stripemode = $mode;
484
485 // list of payment method types
486 $paymentmethodtypes = array("card");
487 $descriptor = dol_trunc($tag, 10, 'right', 'UTF-8', 1);
488 if (getDolGlobalInt('STRIPE_SEPA_DIRECT_DEBIT')) {
489 $paymentmethodtypes[] = "sepa_debit";
490 }
491 if (getDolGlobalInt('STRIPE_KLARNA')) {
492 $paymentmethodtypes[] = "klarna";
493 }
494 if (getDolGlobalInt('STRIPE_BANCONTACT')) {
495 $paymentmethodtypes[] = "bancontact";
496 }
497 if (getDolGlobalInt('STRIPE_IDEAL')) {
498 $paymentmethodtypes[] = "ideal";
499 }
500 if (getDolGlobalInt('STRIPE_GIROPAY')) {
501 $paymentmethodtypes[] = "giropay";
502 }
503 if (getDolGlobalInt('STRIPE_SOFORT')) {
504 $paymentmethodtypes[] = "sofort";
505 }
506 if ($mode == 'terminal') {
507 if (getDolGlobalInt('STRIPE_CARD_PRESENT')) {
508 $paymentmethodtypes = array("card_present");
509 }
510 $stripemode = 'manual';
511 }
513 $descriptioninpaymentintent = $description;
514 // When STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION=2, use automatic_payment_methods
515 // so Stripe Dashboard controls active methods (Klarna, Bancontact, Link, etc.)
516 // and return_url redirect flow works correctly.
517 // In terminal mode, automatic methods are not supported — fallback to manual list.
518 $useautomaticmethods = (getDolGlobalInt('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION') == 2 && $mode != 'terminal');
519 $dataforintent = array_merge(
520 array(
521 "confirm" => $confirmnow,
522 "amount" => $stripeamount,
523 "currency" => $currency_code,
524 "description" => $descriptioninpaymentintent,
525 "metadata" => $metadata,
526 ),
527 $useautomaticmethods ? array(
528 'automatic_payment_methods' => array(
529 'enabled' => true,
530 ),
531 ) : array(
532 'confirmation_method' => $stripemode,
533 'payment_method_types' => $paymentmethodtypes,
534 'setup_future_usage' => 'on_session',
535 )
536 );
537 if ($tag) {
538 $dataforintent["statement_descriptor_suffix"] = dol_trunc($tag, 12, 'right', 'UTF-8', 1); // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
539 $dataforintent["statement_descriptor"] = dol_trunc($tag, 22, 'right', 'UTF-8', 1); // For SEPA, 22 chars, it will take only statement_descriptor, not statement_descriptor_suffix
540 }
541 if (!is_null($customer)) {
542 $dataforintent["customer"] = $customer;
543 }
544 // payment_method =
545 // payment_method_types = array('card')
546 //var_dump($dataforintent);
547 if ($off_session) {
548 unset($dataforintent['setup_future_usage']);
549 // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
550 // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
551 //$dataforintent["setup_future_usage"] = "off_session";
552 $dataforintent["off_session"] = true;
553 }
554 if (getDolGlobalInt('STRIPE_GIROPAY')) {
555 unset($dataforintent['setup_future_usage']);
556 }
557 if (getDolGlobalInt('STRIPE_KLARNA')) {
558 unset($dataforintent['setup_future_usage']);
559 }
560 if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
561 unset($dataforintent['setup_future_usage']);
562 $dataforintent["capture_method"] = "manual";
563 $dataforintent["confirmation_method"] = "manual";
564 }
565 if (!is_null($payment_method)) {
566 $dataforintent["payment_method"] = $payment_method;
567 $description .= ' - '.$payment_method;
568 }
569
570 if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
571 $dataforintent["application_fee_amount"] = $stripefee;
572 }
573 if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
574 $dataforintent["receipt_email"] = $object->thirdparty->email;
575 }
576
577 try {
578 // Force to use the correct API key
579 global $stripearrayofkeysbyenv;
580 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$servicestatus]['secret_key']);
581
582 $arrayofoptions = array();
583 if (empty($noidempotency_key)) {
584 $arrayofoptions["idempotency_key"] = $descriptioninpaymentintent;
585 }
586 // 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.
587 if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
588 $arrayofoptions["stripe_account"] = $key;
589 }
590
591 // Hook to allow external modules to modify Stripe PaymentIntent data before API call.
592 // Can be used to customize statement_descriptor (e.g. structured communication for SEPA),
593 // add metadata, modify description, etc.
594 // Note: $arrayofoptions is not passed for security reasons (contains stripe_account and idempotency_key).
595 $parameters = array(
596 'dataforintent' => $dataforintent,
597 'object' => $object,
598 'tag' => $tag,
599 'amount' => $amount,
600 'currency_code' => $currency_code,
601 'customer' => $customer,
602 'servicestatus' => $servicestatus,
603 );
604 $reshook = $hookmanager->executeHooks('beforeCreateStripePaymentIntent', $parameters, $this);
605 if (!empty($hookmanager->resArray['dataforintent'])) {
606 $dataforintent = $hookmanager->resArray['dataforintent'];
607 }
608
609 dol_syslog(get_class($this)."::getPaymentIntent ".$stripearrayofkeysbyenv[$servicestatus]['publishable_key'], LOG_DEBUG);
610 dol_syslog(get_class($this)."::getPaymentIntent dataforintent to create paymentintent = ".formatLogObject($dataforintent));
611
612 $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
613
614 if ($paymentintent instanceof \Stripe\PaymentIntent) {
615 dol_syslog(get_class($this)."::getPaymentIntent paymentintent is a defined object");
616
617 // Store the payment intent
618 if (is_object($object)) {
619 $paymentintentalreadyexists = 0;
620
621 // Get $customerid and $pkey
622 $customerid = $paymentintent->customer;
623 $pkey = '';
624 if (isset($stripearrayofkeysbyenv[$servicestatus]['publishable_key'])) {
625 $pkey = $stripearrayofkeysbyenv[$servicestatus]['publishable_key'];
626 }
627
628 $LONGTRANSACTIONID = $paymentintent->id.':'.$customerid.'@'.$pkey;
629
630
631 if ($did > 0) {
632 // If a payment request line provided, we do not need to recreate one, we just update it
633 dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
634
635 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
636 $sql .= " ext_payment_site = '".$this->db->escape($service)."',";
637 $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."'"; // TODO Save the long transaction id
638 $sql .= " WHERE rowid = ".((int) $did);
639
640 $resql = $this->db->query($sql);
641 if ($resql) {
642 $paymentintentalreadyexists++;
643 } else {
644 $error++;
645 dol_print_error($this->db);
646 }
647 } else {
648 // Check that payment intent $paymentintent->id is not already recorded.
649 dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
650
651 $sql = "SELECT pi.rowid";
652 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
653 $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
654 $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
655 $sql .= " AND (pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."' OR pi.ext_payment_id = '".$this->db->escape($LONGTRANSACTIONID)."')";
656
657 $resql = $this->db->query($sql);
658 if ($resql) {
659 $num = $this->db->num_rows($resql);
660 if ($num) {
661 $obj = $this->db->fetch_object($resql);
662 if ($obj) {
663 $paymentintentalreadyexists++;
664 }
665 }
666 } else {
667 $error++;
668 dol_print_error($this->db);
669 }
670 }
671
672 // If not, we create it.
673 if (!$error && !$paymentintentalreadyexists) {
674 $now = dol_now();
675 $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
676 // TODO Save the long transaction id in ext_payment_id
677 $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).")";
678 $resql = $this->db->query($sql);
679 if (!$resql) {
680 $error++;
681 $this->error = $this->db->lasterror();
682 dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
683 }
684 }
685 } else {
686 $_SESSION["stripe_payment_intent"] = $paymentintent;
687 }
688 } else {
689 dol_syslog(get_class($this)."::getPaymentIntent create paymentintent did not returned a Stripe\PaymentIntent object", LOG_ERR);
690 }
691 } catch (Stripe\Exception\CardException $e) {
692 $error++;
693 $this->error = $e->getMessage();
694 $this->code = $e->getStripeCode();
695 $this->declinecode = $e->getDeclineCode();
696 } catch (Exception $e) {
697 //var_dump($dataforintent);
698 //var_dump($description);
699 //var_dump($key);
700 //var_dump($paymentintent);
701 //var_dump($e->getMessage());
702 //var_dump($e);
703 $error++;
704 $this->error = $e->getMessage();
705 $this->code = '';
706 $this->declinecode = '';
707 }
708 }
709
710 dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
711
712 if (!$error) {
713 return $paymentintent;
714 } else {
715 return null;
716 }
717 }
718
737 public function getSetupIntent($description, $object, $customer, $key, $servicestatus, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
738 {
739 global $conf;
740
741 $noidempotency_key = 1;
742
743 dol_syslog("getSetupIntent description=".$description.' confirmnow='.formatLogObject($confirmnow), LOG_INFO, 1);
744
745 $error = 0;
746
747 if (empty($servicestatus)) {
748 $service = 'StripeTest';
749 } else {
750 $service = 'StripeLive';
751 }
752
753 $setupintent = null;
754
755 if (empty($setupintent)) { // @phan-suppress-current-line PhanPluginConstantVariableNull
756 $ipaddress = getUserRemoteIP();
757 $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress, 'dol_noidempotency' => (int) $noidempotency_key);
758 if (is_object($object)) {
759 $metadata['dol_type'] = $object->element;
760 $metadata['dol_id'] = $object->id;
761 if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
762 $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
763 }
764 }
765
766 // list of payment method types
767 $paymentmethodtypes = array("card");
768 if (getDolGlobalString('STRIPE_SEPA_DIRECT_DEBIT')) {
769 $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
770 }
771 if (getDolGlobalString('STRIPE_BANCONTACT')) {
772 $paymentmethodtypes[] = "bancontact";
773 }
774 if (getDolGlobalString('STRIPE_IDEAL')) {
775 $paymentmethodtypes[] = "ideal";
776 }
777 // Giropay not possible for setup intent
778 if (getDolGlobalString('STRIPE_SOFORT')) {
779 $paymentmethodtypes[] = "sofort";
780 }
781
783
784 $descriptioninsetupintent = $description;
785
786 $dataforintent = array(
787 "confirm" => $confirmnow, // Do not confirm immediately during creation of intent
788 "payment_method_types" => $paymentmethodtypes, // When payment_method_types is set, return_url is not required but payment mode can't be managed from dashboard
789 /*
790 'return_url' => $dolibarr_main_url_root.'/public/payment/paymentok.php',
791 'automatic_payment_methods' => array(
792 'enabled' => true,
793 'allow_redirects' => 'never',
794 ),
795 */
796 "usage" => "off_session",
797 "metadata" => $metadata
798 );
799 if (!is_null($customer)) {
800 $dataforintent["customer"] = $customer;
801 }
802 if (!is_null($description)) {
803 $dataforintent["description"] = $descriptioninsetupintent;
804 }
805 // payment_method =
806 // payment_method_types = array('card')
807 //var_dump($dataforintent);
808
809 if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
810 $dataforintent["receipt_email"] = $object->thirdparty->email;
811 }
812
813 try {
814 // Force to use the correct API key
815 global $stripearrayofkeysbyenv;
816 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$servicestatus]['secret_key']);
817
818 dol_syslog(get_class($this)."::getSetupIntent ".$stripearrayofkeysbyenv[$servicestatus]['publishable_key'], LOG_DEBUG);
819 dol_syslog(get_class($this)."::getSetupIntent dataforintent to create setupintent = ".formatLogObject($dataforintent));
820
821 // Note: If all data for payment intent are same than a previous one, even if we use 'create', Stripe will return ID of the old existing payment intent.
822 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
823 //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
824 $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
825 } else {
826 //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
827 $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
828 }
829 //var_dump($setupintent->id);
830
831 // Store the setup intent
832 /*if (is_object($object))
833 {
834 $setupintentalreadyexists = 0;
835 // Check that payment intent $setupintent->id is not already recorded.
836 $sql = "SELECT pi.rowid";
837 $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
838 $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
839 $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
840 $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
841
842 dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
843 $resql = $this->db->query($sql);
844 if ($resql) {
845 $num = $this->db->num_rows($resql);
846 if ($num)
847 {
848 $obj = $this->db->fetch_object($resql);
849 if ($obj) $setupintentalreadyexists++;
850 }
851 }
852 else dol_print_error($this->db);
853
854 // If not, we create it.
855 if (! $setupintentalreadyexists)
856 {
857 $now=dol_now();
858 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
859 $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).")";
860 $resql = $this->db->query($sql);
861 if (! $resql)
862 {
863 $error++;
864 $this->error = $this->db->lasterror();
865 dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
866 }
867 }
868 }
869 else
870 {
871 $_SESSION["stripe_setup_intent"] = $setupintent;
872 }*/
873 } catch (Exception $e) {
874 //var_dump($dataforintent);
875 //var_dump($description);
876 //var_dump($key);
877 //var_dump($setupintent);
878 //var_dump($e->getMessage());
879 $error++;
880 $this->error = $e->getMessage();
881 }
882 }
883
884 if (!$error) {
885 dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
886 return $setupintent;
887 } else {
888 dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
889 return null;
890 }
891 }
892
893
904 public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
905 {
906 global $conf, $langs;
907
908 $card = null;
909
910 $sql = "SELECT sa.stripe_card_ref, sa.proprio as owner_name, sa.exp_date_month, sa.exp_date_year, sa.number, sa.cvn"; // stripe_card_ref is card_....
911 $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
912 $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
913 $sql .= " AND sa.type = 'card'";
914
915 dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
916 $resql = $this->db->query($sql);
917 if ($resql) {
918 $num = $this->db->num_rows($resql);
919 if ($num) {
920 $obj = $this->db->fetch_object($resql);
921 $cardref = $obj->stripe_card_ref;
922 dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
923 if ($cardref) {
924 try {
925 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
926 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
927 $card = $cu->sources->retrieve($cardref);
928 } else {
929 $card = \Stripe\PaymentMethod::retrieve($cardref);
930 }
931 } else {
932 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
933 //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
934 $card = $cu->sources->retrieve($cardref);
935 } else {
936 //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
937 $card = \Stripe\PaymentMethod::retrieve($cardref);
938 }
939 }
940 } catch (Exception $e) {
941 $this->error = $e->getMessage();
942 dol_syslog($this->error, LOG_WARNING);
943 }
944 } elseif ($createifnotlinkedtostripe) {
945 // Deprecated with new Stripe API and SCA. We should not use anymore this part of code now.
946 $exp_date_month = $obj->exp_date_month;
947 $exp_date_year = $obj->exp_date_year;
948 $number = $obj->number;
949 $cvc = $obj->cvn; // cvn in database, cvc for stripe
950 $cardholdername = $obj->owner_name;
951
952 $ipaddress = getUserRemoteIP();
953
954 $dataforcard = array(
955 "source" => array(
956 'object' => 'card',
957 'exp_month' => $exp_date_month,
958 'exp_year' => $exp_date_year,
959 'number' => $number,
960 'cvc' => $cvc,
961 'name' => $cardholdername
962 ),
963 "metadata" => array(
964 'dol_type' => $object->element,
965 'dol_id' => $object->id,
966 'dol_version' => DOL_VERSION,
967 'dol_entity' => $conf->entity,
968 'ipaddress' => $ipaddress
969 )
970 );
971
972 //$a = \Stripe\Stripe::getApiKey();
973 //var_dump($a);
974 //var_dump($stripeacc);exit;
975 try {
976 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
977 if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) {
978 dol_syslog("Try to create card with dataforcard = ".formatLogObject($dataforcard));
979 $card = $cu->sources->create($dataforcard);
980 if (!$card) {
981 $this->error = 'Creation of card on Stripe has failed';
982 }
983 } else {
984 $connect = '';
985 if (!empty($stripeacc)) {
986 $connect = $stripeacc.'/';
987 }
988 $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
989 if ($status) {
990 $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
991 }
992 $urtoswitchonstripe = '<a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
993
994 //dol_syslog("Error: This case is not supported", LOG_ERR);
995 $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}'));
996 }
997 } else {
998 if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) {
999 dol_syslog("Try to create card with dataforcard = ".formatLogObject($dataforcard));
1000 $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
1001 if (!$card) {
1002 $this->error = 'Creation of card on Stripe has failed';
1003 }
1004 } else {
1005 $connect = '';
1006 if (!empty($stripeacc)) {
1007 $connect = $stripeacc.'/';
1008 }
1009 $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
1010 if ($status) {
1011 $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
1012 }
1013 $urtoswitchonstripe = '<a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
1014
1015 //dol_syslog("Error: This case is not supported", LOG_ERR);
1016 $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}'));
1017 }
1018 }
1019
1020 if ($card) {
1021 $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
1022 $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
1023 $sql .= " country_code = '".$this->db->escape($card->country)."',";
1024 $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
1025 $sql .= " WHERE rowid = ".((int) $object->id);
1026 $sql .= " AND type = 'card'";
1027 $resql = $this->db->query($sql);
1028 if (!$resql) {
1029 $this->error = $this->db->lasterror();
1030 }
1031 }
1032 } catch (Exception $e) {
1033 $this->error = $e->getMessage();
1034 dol_syslog($this->error, LOG_WARNING);
1035 }
1036 }
1037 }
1038 } else {
1039 dol_print_error($this->db);
1040 }
1041
1042 return $card;
1043 }
1044
1045
1056 public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
1057 {
1058 global $conf;
1059 $sepa = null;
1060
1061 $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
1062 $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
1063 $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
1064 $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
1065
1066 $soc = new Societe($this->db);
1067 $soc->fetch($object->fk_soc);
1068
1069 dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
1070 $resql = $this->db->query($sql);
1071 if ($resql) {
1072 $num = $this->db->num_rows($resql);
1073 if ($num) {
1074 $obj = $this->db->fetch_object($resql);
1075 $cardref = $obj->stripe_card_ref;
1076
1077 dol_syslog(get_class($this)."::sepaStripe paymentmode=".$cardref);
1078
1079 if ($cardref) {
1080 try {
1081 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
1082 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
1083 $sepa = $cu->sources->retrieve($cardref);
1084 } else {
1085 $sepa = \Stripe\PaymentMethod::retrieve($cardref);
1086 }
1087 } else {
1088 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
1089 //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
1090 $sepa = $cu->sources->retrieve($cardref);
1091 } else {
1092 //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
1093 $sepa = \Stripe\PaymentMethod::retrieve($cardref);
1094 }
1095 }
1096 } catch (Exception $e) {
1097 $this->error = $e->getMessage();
1098 dol_syslog($this->error, LOG_WARNING);
1099 }
1100 } elseif ($createifnotlinkedtostripe) {
1101 $iban = dolDecrypt($obj->iban);
1102 $ipaddress = getUserRemoteIP();
1103 $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
1104 if (is_object($object)) {
1105 $metadata['dol_type'] = $object->element;
1106 $metadata['dol_id'] = $object->id;
1107 $metadata['dol_thirdparty_id'] = $soc->id;
1108 }
1109
1110 $description = 'SEPA for IBAN '.$iban;
1111
1112 $dataforcard = array(
1113 'type' => 'sepa_debit',
1114 "sepa_debit" => array('iban' => $iban),
1115 'billing_details' => array(
1116 'name' => $soc->name,
1117 'email' => !empty($soc->email) ? $soc->email : "",
1118 ),
1119 "metadata" => $metadata
1120 );
1121 // Complete owner name
1122 if (!empty($soc->town)) {
1123 $dataforcard['billing_details']['address']['city'] = $soc->town;
1124 }
1125 if (!empty($soc->country_code)) {
1126 $dataforcard['billing_details']['address']['country'] = $soc->country_code;
1127 }
1128 if (!empty($soc->address)) {
1129 $dataforcard['billing_details']['address']['line1'] = $soc->address;
1130 }
1131 if (!empty($soc->zip)) {
1132 $dataforcard['billing_details']['address']['postal_code'] = $soc->zip;
1133 }
1134 if (!empty($soc->state)) {
1135 $dataforcard['billing_details']['address']['state'] = $soc->state;
1136 }
1137
1138 //$a = \Stripe\Stripe::getApiKey();
1139 //var_dump($a);var_dump($stripeacc);exit;
1140 try {
1141 dol_syslog("Try to create sepa_debit");
1142
1143 $service = 'StripeTest';
1144 $servicestatus = 0;
1145 if (getDolGlobalString('STRIPE_LIVE')/* && !GETPOST('forcesandbox', 'alpha') */) {
1146 $service = 'StripeLive';
1147 $servicestatus = 1;
1148 }
1149 // Force to use the correct API key
1150 global $stripearrayofkeysbyenv;
1151 $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
1152
1153 dol_syslog("Try to create sepa_debit with data = ".formatLogObject($dataforcard));
1154
1155 $s = new \Stripe\StripeClient($stripeacc);
1156
1157 //var_dump($dataforcard);exit;
1158
1159 $sepa = $s->paymentMethods->create($dataforcard);
1160 if (!$sepa) {
1161 $this->error = 'Creation of payment method sepa_debit on Stripe has failed';
1162 dol_syslog($this->error, LOG_ERR);
1163 } else {
1164 // link customer and src
1165 //$cs = $this->getSetupIntent($description, $soc, $cu, '', $status);
1166 $dataforintent = array(0 => ['description' => $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata' => $metadata);
1167
1168 $cs = $s->setupIntents->create($dataforintent);
1169 //$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]);
1170
1171 if (!$cs) {
1172 $this->error = 'Link SEPA <-> setupIntent->create failed';
1173 dol_syslog($this->error, LOG_ERR);
1174 $cs2 = null;
1175 } else {
1176 // note: $cs->mandate contains ID of mandate on Stripe side
1177 $cs2 = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]);
1178 if (!$cs2) {
1179 $this->error = 'Link SEPA <-> setupIntent->confirm failed';
1180 dol_syslog($this->error, LOG_ERR);
1181 }
1182 }
1183
1184 if ($cs && $cs2) {
1185 dol_syslog("Update the payment mode of the customer");
1186
1187 // print json_encode($sepa);
1188
1189 // Save the Stripe payment mode ID into the Dolibarr database
1190 $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
1191 $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."',";
1192 $sql .= " card_type = 'sepa_debit',";
1193 $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "',";
1194 $sql .= " ext_payment_site = '".$this->db->escape($service)."'";
1195 if (!empty($cs->mandate)) {
1196 $mandateservice = new \Stripe\Mandate($stripeacc);
1197 $mandate = $mandateservice->retrieve($cs->mandate);
1198 if (is_object($mandate) && is_object($mandate->payment_method_details) && is_object($mandate->payment_method_details->sepa_debit)) {
1199 $refmandate = $mandate->payment_method_details->sepa_debit->reference;
1200 //$urlmandate = $mandate->payment_method_details->sepa_debit->url;
1201 $sql .= ", rum = '".$this->db->escape($refmandate)."'";
1202 }
1203 $sql .= ", comment = '".$this->db->escape($cs->mandate)."'";
1204 $sql .= ", date_rum = '".$this->db->idate(dol_now())."'";
1205 }
1206 $sql .= " WHERE rowid = ".((int) $object->id);
1207 $sql .= " AND type = 'ban'";
1208 $resql = $this->db->query($sql);
1209 if (!$resql) {
1210 $this->error = $this->db->lasterror();
1211 }
1212 }
1213 }
1214 } catch (Exception $e) {
1215 $sepa = null;
1216 $this->error = 'Stripe error: '.$e->getMessage().'. Check the BAN information.';
1217 dol_syslog($this->error, LOG_WARNING); // Error from Stripe, so a warning on Dolibarr
1218 }
1219 }
1220 }
1221 } else {
1222 dol_print_error($this->db);
1223 }
1224
1225 return $sepa;
1226 }
1227}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
global $dolibarr_main_url_root
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class for CompanyPaymentMode.
Class for SocieteAccount.
Class to manage third parties objects (customers, suppliers, prospects...)
Stripe class @TODO No reason to extend CommonObject.
convertAmount($amount, $currency_code, $direction=0)
Convert an amount in Stripe format into an amount into standard amount.
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 (create it if it doesn't exists and $createifnotlinkedt...
customerStripe($object, $key='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe customer of a thirdparty (with option to create it in Stripe if not linked yet).
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 ...
__construct($db)
Constructor.
getPaymentIntent($amount, $currency_code, $tag, $description='', $object=null, $customer=null, $key=null, $servicestatus=0, $usethirdpartyemailforreceiptemail=0, $mode='automatic', $confirmnow=false, $payment_method=null, $off_session=0, $noidempotency_key=1, $did=0)
Get the Stripe payment intent.
getStripeAccount($mode='StripeTest', $fk_soc=0, $entity=-1)
Return main company OAuth Connect stripe account.
getSetupIntent($description, $object, $customer, $key, $servicestatus, $usethirdpartyemailforreceiptemail=0, $confirmnow=false)
Get the Stripe payment intent.
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('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_now($mode='gmt')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) formatLogObject($data)
Return a string serialized to be output on log with dol_syslog() An option allow to output log in one...
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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 a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dolDecrypt($chain, $key='', $patterntotest='')
Decode a string with a symmetric encryption.