dolibarr 21.0.0-alpha
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 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 env
26
27
32class Stripe extends CommonObject
33{
37 public $rowid;
38
42 public $fk_soc;
43
47 public $fk_key;
48
52 public $id;
53
57 public $mode;
58
62 public $entity;
63
68 public $result;
69
73 public $type;
74
78 public $code;
79
83 public $declinecode;
84
88 public $message;
89
95 public function __construct($db)
96 {
97 $this->db = $db;
98 }
99
100
109 public function getStripeAccount($mode = 'StripeTest', $fk_soc = 0, $entity = -1)
110 {
111 global $conf;
112
113 $key = '';
114 if ($entity < 0) {
115 $entity = $conf->entity;
116 }
117
118 $sql = "SELECT tokenstring";
119 $sql .= " FROM ".MAIN_DB_PREFIX."oauth_token";
120 $sql .= " WHERE service = '".$this->db->escape($mode)."'";
121 $sql .= " AND entity = ".((int) $entity);
122 if ($fk_soc > 0) {
123 $sql .= " AND fk_soc = ".((int) $fk_soc);
124 } else {
125 $sql .= " AND fk_soc IS NULL";
126 }
127 $sql .= " AND fk_user IS NULL AND fk_adherent IS NULL";
128
129 dol_syslog(get_class($this)."::getStripeAccount", LOG_DEBUG);
130
131 $result = $this->db->query($sql);
132 if ($result) {
133 if ($this->db->num_rows($result)) {
134 $obj = $this->db->fetch_object($result);
135 $tokenstring = $obj->tokenstring;
136
137 if ($tokenstring) {
138 $tmparray = json_decode($tokenstring);
139 $key = empty($tmparray->stripe_user_id) ? '' : $tmparray->stripe_user_id;
140 }
141 } else {
142 $tokenstring = '';
143 }
144 } else {
145 dol_print_error($this->db);
146 }
147
148 dol_syslog("No dedicated Stripe Connect account available for entity ".$conf->entity);
149
150 return $key;
151 }
152
161 public function getStripeCustomerAccount($id, $status = 0, $site_account = '')
162 {
163 include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
164 $societeaccount = new SocieteAccount($this->db);
165 return $societeaccount->getCustomerAccount($id, 'stripe', $status, $site_account); // Get thirdparty cus_...
166 }
167
168
179 public function customerStripe(CommonObject $object, $key = '', $status = 0, $createifnotlinkedtostripe = 0)
180 {
181 global $conf, $user;
182
183 if (empty($object->id)) {
184 dol_syslog("customerStripe is called with the parameter object that is not loaded");
185 return null;
186 }
187
188 $customer = null;
189
190 // Force to use the correct API key
191 global $stripearrayofkeysbyenv;
192 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
193
194 $sql = "SELECT sa.key_account as key_account, sa.entity"; // key_account is cus_....
195 $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
196 $sql .= " WHERE sa.fk_soc = ".((int) $object->id);
197 $sql .= " AND sa.entity IN (".getEntity('societe').")";
198 $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
199 $sql .= " AND (sa.site_account IS NULL OR sa.site_account = '' OR sa.site_account = '".$this->db->escape($stripearrayofkeysbyenv[$status]['publishable_key'])."')";
200 $sql .= " AND sa.key_account IS NOT NULL AND sa.key_account <> ''";
201
202 dol_syslog(get_class($this)."::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG);
203 $resql = $this->db->query($sql);
204 if ($resql) {
205 $num = $this->db->num_rows($resql);
206 if ($num) {
207 $obj = $this->db->fetch_object($resql);
208 $tiers = $obj->key_account;
209
210 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']);
211
212 try {
213 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
214 //$customer = \Stripe\Customer::retrieve("$tiers");
215 $customer = \Stripe\Customer::retrieve(array('id' => "$tiers", 'expand[]' => 'sources'));
216 } else {
217 //$customer = \Stripe\Customer::retrieve("$tiers", array("stripe_account" => $key));
218 $customer = \Stripe\Customer::retrieve(array('id' => "$tiers", 'expand[]' => 'sources'), array("stripe_account" => $key));
219 }
220 } catch (Exception $e) {
221 // 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.'
222 $this->error = $e->getMessage();
223 }
224 } elseif ($createifnotlinkedtostripe) {
225 $ipaddress = getUserRemoteIP();
226
227 $dataforcustomer = array(
228 "email" => $object->email,
229 "description" => $object->name,
230 "metadata" => array('dol_id' => $object->id, 'dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress)
231 );
232
233 $vatcleaned = $object->tva_intra ? $object->tva_intra : null;
234
235 /*
236 $taxinfo = array('type'=>'vat');
237 if ($vatcleaned)
238 {
239 $taxinfo["tax_id"] = $vatcleaned;
240 }
241 // We force data to "null" if not defined as expected by Stripe
242 if (empty($vatcleaned)) $taxinfo=null;
243 $dataforcustomer["tax_info"] = $taxinfo;
244 */
245
246 //$a = \Stripe\Stripe::getApiKey();
247 //var_dump($a);var_dump($key);exit;
248 try {
249 // Force to use the correct API key
250 global $stripearrayofkeysbyenv;
251 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
252
253 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
254 $customer = \Stripe\Customer::create($dataforcustomer);
255 } else {
256 $customer = \Stripe\Customer::create($dataforcustomer, array("stripe_account" => $key));
257 }
258
259 // Create the VAT record in Stripe
260 if (getDolGlobalString('STRIPE_SAVE_TAX_IDS')) { // We setup to save Tax info on Stripe side. Warning: This may result in error when saving customer
261 if (!empty($vatcleaned)) {
262 $isineec = isInEEC($object);
263 if ($object->country_code && $isineec) {
264 //$taxids = $customer->allTaxIds($customer->id);
265 $customer->createTaxId($customer->id, array('type' => 'eu_vat', 'value' => $vatcleaned));
266 }
267 }
268 }
269
270 // Create customer in Dolibarr
271 $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_account (fk_soc, login, key_account, site, site_account, status, entity, date_creation, fk_user_creat)";
272 $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).")";
273 $resql = $this->db->query($sql);
274 if (!$resql) {
275 $this->error = $this->db->lasterror();
276 }
277 } catch (Exception $e) {
278 $this->error = $e->getMessage();
279 }
280 }
281 } else {
282 dol_print_error($this->db);
283 }
284
285 return $customer;
286 }
287
296 public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0)
297 {
298 $stripepaymentmethod = null;
299
300 try {
301 // Force to use the correct API key
302 global $stripearrayofkeysbyenv;
303 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
304 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
305 $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id);
306 } else {
307 $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id, array("stripe_account" => $key));
308 }
309 } catch (Exception $e) {
310 $this->error = $e->getMessage();
311 }
312
313 return $stripepaymentmethod;
314 }
315
324 public function getSelectedReader($reader, $key = '', $status = 0)
325 {
326 $selectedreader = null;
327
328 try {
329 // Force to use the correct API key
330 global $stripearrayofkeysbyenv;
331 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
332 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
333 $selectedreader = \Stripe\Terminal\Reader::retrieve((string) $reader);
334 } else {
335 $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve((string) $reader, array("stripe_account" => $key));
336 }
337 } catch (Exception $e) {
338 $this->error = $e->getMessage();
339 }
340
341 return $selectedreader;
342 }
343
370 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)
371 {
372 global $conf, $user;
373
374 dol_syslog(get_class($this)."::getPaymentIntent", LOG_INFO, 1);
375
376 $error = 0;
377
378 if (empty($status)) {
379 $service = 'StripeTest';
380 } else {
381 $service = 'StripeLive';
382 }
383
384 $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
385 if (!in_array($currency_code, $arrayzerounitcurrency)) {
386 $stripeamount = $amount * 100;
387 } else {
388 $stripeamount = $amount;
389 }
390
391 $fee = 0;
392 if (getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT")) {
393 $fee = $amount * ((float) getDolGlobalString("STRIPE_APPLICATION_FEE_PERCENT", '0') / 100) + (float) getDolGlobalString("STRIPE_APPLICATION_FEE", '0');
394 }
395 if ($fee >= (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') && (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0') > (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
396 $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MAXIMAL", '0');
397 } elseif ($fee < (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0')) {
398 $fee = (float) getDolGlobalString("STRIPE_APPLICATION_FEE_MINIMAL", '0');
399 }
400 if (!in_array($currency_code, $arrayzerounitcurrency)) {
401 $stripefee = round($fee * 100);
402 } else {
403 $stripefee = round($fee);
404 }
405
406 $paymentintent = null;
407
408 if (is_object($object) && getDolGlobalInt('STRIPE_REUSE_EXISTING_INTENT_IF_FOUND') && !getDolGlobalInt('STRIPE_CARD_PRESENT')) {
409 // Warning. If a payment was tried and failed, a payment intent was created.
410 // 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.
411 // 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
412 // automatically return the existing payment intent if idempotency is provided when we try to create the new one.
413 // 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)
414
415 $sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
416 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
417 $sql .= " WHERE pi.fk_facture = ".((int) $object->id);
418 $sql .= " AND pi.sourcetype = '".$this->db->escape($object->element)."'";
419 $sql .= " AND pi.entity IN (".getEntity('societe').")";
420 $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
421
422 dol_syslog(get_class($this)."::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
423 $resql = $this->db->query($sql);
424 if ($resql) {
425 $num = $this->db->num_rows($resql);
426 if ($num) {
427 $obj = $this->db->fetch_object($resql);
428 $intent = $obj->ext_payment_id;
429
430 dol_syslog(get_class($this)."::getPaymentIntent found existing payment intent record");
431
432 // Force to use the correct API key
433 global $stripearrayofkeysbyenv;
434 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
435
436 try {
437 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
438 $paymentintent = \Stripe\PaymentIntent::retrieve($intent);
439 } else {
440 $paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
441 }
442 } catch (Exception $e) {
443 $error++;
444 $this->error = $e->getMessage();
445 }
446 }
447 }
448 }
449
450 if (empty($paymentintent)) {
451 // Try to create intent. See https://stripe.com/docs/api/payment_intents/create
452 $ipaddress = getUserRemoteIP();
453 $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
454 if (is_object($object)) {
455 $metadata['dol_type'] = $object->element;
456 $metadata['dol_id'] = $object->id;
457 if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
458 $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
459 }
460 }
461
462 // list of payment method types
463 $paymentmethodtypes = array("card");
464 $descriptor = dol_trunc($tag, 10, 'right', 'UTF-8', 1);
465 if (getDolGlobalInt('STRIPE_SEPA_DIRECT_DEBIT')) {
466 $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
467 //$descriptor = preg_replace('/ref=[^:=]+/', '', $descriptor); // Clean ref
468 }
469 if (getDolGlobalInt('STRIPE_KLARNA')) {
470 $paymentmethodtypes[] = "klarna";
471 }
472 if (getDolGlobalInt('STRIPE_BANCONTACT')) {
473 $paymentmethodtypes[] = "bancontact";
474 }
475 if (getDolGlobalInt('STRIPE_IDEAL')) {
476 $paymentmethodtypes[] = "ideal";
477 }
478 if (getDolGlobalInt('STRIPE_GIROPAY')) {
479 $paymentmethodtypes[] = "giropay";
480 }
481 if (getDolGlobalInt('STRIPE_SOFORT')) {
482 $paymentmethodtypes[] = "sofort";
483 }
484 if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
485 $paymentmethodtypes = array("card_present");
486 }
487
488 global $dolibarr_main_url_root;
489
490 $dataforintent = array(
491 "confirm" => $confirmnow, // try to confirm immediately after create (if conditions are ok)
492 "confirmation_method" => $mode,
493 "amount" => $stripeamount,
494 "currency" => $currency_code,
495 "payment_method_types" => $paymentmethodtypes, // When payment_method_types is set, return_url is not required but payment mode can't be managed from dashboard
496 /*
497 'return_url' => $dolibarr_main_url_root.'/public/payment/paymentok.php',
498 'automatic_payment_methods' => array(
499 'enabled' => true,
500 'allow_redirects' => 'never',
501 ),
502 */
503 "description" => $description,
504 //"save_payment_method" => true,
505 "setup_future_usage" => "on_session",
506 "metadata" => $metadata
507 );
508 if ($descriptor) {
509 $dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
510 $dataforintent["statement_descriptor"] = $descriptor; // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
511 }
512 if (!is_null($customer)) {
513 $dataforintent["customer"] = $customer;
514 }
515 // payment_method =
516 // payment_method_types = array('card')
517 //var_dump($dataforintent);
518 if ($off_session) {
519 unset($dataforintent['setup_future_usage']);
520 // We can't use both "setup_future_usage" = "off_session" and "off_session" = true.
521 // Because $off_session parameter is dedicated to create paymentintent off_line (and not future payment), we need to use "off_session" = true.
522 //$dataforintent["setup_future_usage"] = "off_session";
523 $dataforintent["off_session"] = true;
524 }
525 if (getDolGlobalInt('STRIPE_GIROPAY')) {
526 unset($dataforintent['setup_future_usage']);
527 }
528 if (getDolGlobalInt('STRIPE_KLARNA')) {
529 unset($dataforintent['setup_future_usage']);
530 }
531 if (getDolGlobalInt('STRIPE_CARD_PRESENT') && $mode == 'terminal') {
532 unset($dataforintent['setup_future_usage']);
533 $dataforintent["capture_method"] = "manual";
534 $dataforintent["confirmation_method"] = "manual";
535 }
536 if (!is_null($payment_method)) {
537 $dataforintent["payment_method"] = $payment_method;
538 $description .= ' - '.$payment_method;
539 }
540
541 if ($conf->entity != getDolGlobalInt('STRIPECONNECT_PRINCIPAL') && $stripefee > 0) {
542 $dataforintent["application_fee_amount"] = $stripefee;
543 }
544 if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
545 $dataforintent["receipt_email"] = $object->thirdparty->email;
546 }
547
548 try {
549 // Force to use the correct API key
550 global $stripearrayofkeysbyenv;
551 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
552
553 $arrayofoptions = array();
554 if (empty($noidempotency_key)) {
555 $arrayofoptions["idempotency_key"] = $description;
556 }
557 // 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.
558 if (!empty($key)) { // If the Stripe connect account not set, we use common API usage
559 $arrayofoptions["stripe_account"] = $key;
560 }
561
562 dol_syslog("dataforintent to create paymentintent = ".var_export($dataforintent, true));
563
564 $paymentintent = \Stripe\PaymentIntent::create($dataforintent, $arrayofoptions);
565
566 // Store the payment intent
567 if (is_object($object)) {
568 $paymentintentalreadyexists = 0;
569
570 if ($did > 0) {
571 // If a payment request line provided, we do not need to recreate one, we just update it
572 dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
573
574 $sql = "UPDATE ".MAIN_DB_PREFIX."prelevement_demande SET";
575 $sql .= " ext_payment_site = '".$this->db->escape($service)."',";
576 $sql .= " ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
577 $sql .= " WHERE rowid = ".((int) $did);
578
579 $resql = $this->db->query($sql);
580 if ($resql) {
581 $paymentintentalreadyexists++;
582 } else {
583 $error++;
584 dol_print_error($this->db);
585 }
586 } else {
587 // Check that payment intent $paymentintent->id is not already recorded.
588 dol_syslog(get_class($this)."::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
589
590 $sql = "SELECT pi.rowid";
591 $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pi";
592 $sql .= " WHERE pi.entity IN (".getEntity('societe').")";
593 $sql .= " AND pi.ext_payment_site = '".$this->db->escape($service)."'";
594 $sql .= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
595
596 $resql = $this->db->query($sql);
597 if ($resql) {
598 $num = $this->db->num_rows($resql);
599 if ($num) {
600 $obj = $this->db->fetch_object($resql);
601 if ($obj) {
602 $paymentintentalreadyexists++;
603 }
604 }
605 } else {
606 $error++;
607 dol_print_error($this->db);
608 }
609 }
610
611 // If not, we create it.
612 if (!$error && !$paymentintentalreadyexists) {
613 $now = dol_now();
614 $sql = "INSERT INTO ".MAIN_DB_PREFIX."prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site, amount)";
615 $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).")";
616 $resql = $this->db->query($sql);
617 if (!$resql) {
618 $error++;
619 $this->error = $this->db->lasterror();
620 dol_syslog(get_class($this)."::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.", LOG_ERR);
621 }
622 }
623 } else {
624 $_SESSION["stripe_payment_intent"] = $paymentintent;
625 }
626 } catch (Stripe\Exception\CardException $e) {
627 $error++;
628 $this->error = $e->getMessage();
629 $this->code = $e->getStripeCode();
630 $this->declinecode = $e->getDeclineCode();
631 } catch (Exception $e) {
632 //var_dump($dataforintent);
633 //var_dump($description);
634 //var_dump($key);
635 //var_dump($paymentintent);
636 //var_dump($e->getMessage());
637 //var_dump($e);
638 $error++;
639 $this->error = $e->getMessage();
640 $this->code = '';
641 $this->declinecode = '';
642 }
643 }
644
645 dol_syslog(get_class($this)."::getPaymentIntent return error=".$error." this->error=".$this->error, LOG_INFO, -1);
646
647 if (!$error) {
648 return $paymentintent;
649 } else {
650 return null;
651 }
652 }
653
672 public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
673 {
674 global $conf;
675
676 dol_syslog("getSetupIntent description=".$description.' confirmnow='.json_encode($confirmnow), LOG_INFO, 1);
677
678 $error = 0;
679
680 if (empty($status)) {
681 $service = 'StripeTest';
682 } else {
683 $service = 'StripeLive';
684 }
685
686 $setupintent = null;
687
688 if (empty($setupintent)) {
689 $ipaddress = getUserRemoteIP();
690 $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
691 if (is_object($object)) {
692 $metadata['dol_type'] = $object->element;
693 $metadata['dol_id'] = $object->id;
694 if (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
695 $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
696 }
697 }
698
699 // list of payment method types
700 $paymentmethodtypes = array("card");
701 if (getDolGlobalString('STRIPE_SEPA_DIRECT_DEBIT')) {
702 $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC())
703 }
704 if (getDolGlobalString('STRIPE_BANCONTACT')) {
705 $paymentmethodtypes[] = "bancontact";
706 }
707 if (getDolGlobalString('STRIPE_IDEAL')) {
708 $paymentmethodtypes[] = "ideal";
709 }
710 // Giropay not possible for setup intent
711 if (getDolGlobalString('STRIPE_SOFORT')) {
712 $paymentmethodtypes[] = "sofort";
713 }
714
715 global $dolibarr_main_url_root;
716
717 $dataforintent = array(
718 "confirm" => $confirmnow, // Do not confirm immediately during creation of intent
719 "payment_method_types" => $paymentmethodtypes, // When payment_method_types is set, return_url is not required but payment mode can't be managed from dashboard
720 /*
721 'return_url' => $dolibarr_main_url_root.'/public/payment/paymentok.php',
722 'automatic_payment_methods' => array(
723 'enabled' => true,
724 'allow_redirects' => 'never',
725 ),
726 */
727 "usage" => "off_session",
728 "metadata" => $metadata
729 );
730 if (!is_null($customer)) {
731 $dataforintent["customer"] = $customer;
732 }
733 if (!is_null($description)) {
734 $dataforintent["description"] = $description;
735 }
736 // payment_method =
737 // payment_method_types = array('card')
738 //var_dump($dataforintent);
739
740 if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) {
741 $dataforintent["receipt_email"] = $object->thirdparty->email;
742 }
743
744 try {
745 // Force to use the correct API key
746 global $stripearrayofkeysbyenv;
747 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
748
749 dol_syslog("getSetupIntent ".$stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG);
750
751 // 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.
752 if (empty($key)) { // If the Stripe connect account not set, we use common API usage
753 //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
754 $setupintent = \Stripe\SetupIntent::create($dataforintent, array());
755 } else {
756 //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
757 $setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
758 }
759 //var_dump($setupintent->id);
760
761 // Store the setup intent
762 /*if (is_object($object))
763 {
764 $setupintentalreadyexists = 0;
765 // Check that payment intent $setupintent->id is not already recorded.
766 $sql = "SELECT pi.rowid";
767 $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi";
768 $sql.= " WHERE pi.entity IN (".getEntity('societe').")";
769 $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'";
770 $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
771
772 dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG);
773 $resql = $this->db->query($sql);
774 if ($resql) {
775 $num = $this->db->num_rows($resql);
776 if ($num)
777 {
778 $obj = $this->db->fetch_object($resql);
779 if ($obj) $setupintentalreadyexists++;
780 }
781 }
782 else dol_print_error($this->db);
783
784 // If not, we create it.
785 if (! $setupintentalreadyexists)
786 {
787 $now=dol_now();
788 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
789 $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).")";
790 $resql = $this->db->query($sql);
791 if (! $resql)
792 {
793 $error++;
794 $this->error = $this->db->lasterror();
795 dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
796 }
797 }
798 }
799 else
800 {
801 $_SESSION["stripe_setup_intent"] = $setupintent;
802 }*/
803 } catch (Exception $e) {
804 //var_dump($dataforintent);
805 //var_dump($description);
806 //var_dump($key);
807 //var_dump($setupintent);
808 //var_dump($e->getMessage());
809 $error++;
810 $this->error = $e->getMessage();
811 }
812 }
813
814 if (!$error) {
815 dol_syslog("getSetupIntent ".(is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1);
816 return $setupintent;
817 } else {
818 dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
819 return null;
820 }
821 }
822
823
834 public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
835 {
836 global $conf, $user, $langs;
837
838 $card = null;
839
840 $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_....
841 $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
842 $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
843 $sql .= " AND sa.type = 'card'";
844
845 dol_syslog(get_class($this)."::cardStripe search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
846 $resql = $this->db->query($sql);
847 if ($resql) {
848 $num = $this->db->num_rows($resql);
849 if ($num) {
850 $obj = $this->db->fetch_object($resql);
851 $cardref = $obj->stripe_card_ref;
852 dol_syslog(get_class($this)."::cardStripe cardref=".$cardref);
853 if ($cardref) {
854 try {
855 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
856 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
857 $card = $cu->sources->retrieve($cardref);
858 } else {
859 $card = \Stripe\PaymentMethod::retrieve($cardref);
860 }
861 } else {
862 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
863 //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
864 $card = $cu->sources->retrieve($cardref);
865 } else {
866 //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
867 $card = \Stripe\PaymentMethod::retrieve($cardref);
868 }
869 }
870 } catch (Exception $e) {
871 $this->error = $e->getMessage();
872 dol_syslog($this->error, LOG_WARNING);
873 }
874 } elseif ($createifnotlinkedtostripe) {
875 // Deprecated with new Stripe API and SCA. We should not use anymore this part of code now.
876 $exp_date_month = $obj->exp_date_month;
877 $exp_date_year = $obj->exp_date_year;
878 $number = $obj->number;
879 $cvc = $obj->cvn; // cvn in database, cvc for stripe
880 $cardholdername = $obj->proprio;
881
882 $ipaddress = getUserRemoteIP();
883
884 $dataforcard = array(
885 "source" => array(
886 'object' => 'card',
887 'exp_month' => $exp_date_month,
888 'exp_year' => $exp_date_year,
889 'number' => $number,
890 'cvc' => $cvc,
891 'name' => $cardholdername
892 ),
893 "metadata" => array(
894 'dol_type' => $object->element,
895 'dol_id' => $object->id,
896 'dol_version' => DOL_VERSION,
897 'dol_entity' => $conf->entity,
898 'ipaddress' => $ipaddress
899 )
900 );
901
902 //$a = \Stripe\Stripe::getApiKey();
903 //var_dump($a);
904 //var_dump($stripeacc);exit;
905 try {
906 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
907 if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) {
908 dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
909 $card = $cu->sources->create($dataforcard);
910 if (!$card) {
911 $this->error = 'Creation of card on Stripe has failed';
912 }
913 } else {
914 $connect = '';
915 if (!empty($stripeacc)) {
916 $connect = $stripeacc.'/';
917 }
918 $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
919 if ($status) {
920 $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
921 }
922 $urtoswitchonstripe = '<a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
923
924 //dol_syslog("Error: This case is not supported", LOG_ERR);
925 $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}'));
926 }
927 } else {
928 if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) {
929 dol_syslog("Try to create card with dataforcard = ".json_encode($dataforcard));
930 $card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
931 if (!$card) {
932 $this->error = 'Creation of card on Stripe has failed';
933 }
934 } else {
935 $connect = '';
936 if (!empty($stripeacc)) {
937 $connect = $stripeacc.'/';
938 }
939 $url = 'https://dashboard.stripe.com/'.$connect.'test/customers/'.$cu->id;
940 if ($status) {
941 $url = 'https://dashboard.stripe.com/'.$connect.'customers/'.$cu->id;
942 }
943 $urtoswitchonstripe = '<a href="'.$url.'" target="_stripe">'.img_picto($langs->trans('ShowInStripe'), 'globe').'</a>';
944
945 //dol_syslog("Error: This case is not supported", LOG_ERR);
946 $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}'));
947 }
948 }
949
950 if ($card) {
951 $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
952 $sql .= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
953 $sql .= " country_code = '".$this->db->escape($card->country)."',";
954 $sql .= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
955 $sql .= " WHERE rowid = ".((int) $object->id);
956 $sql .= " AND type = 'card'";
957 $resql = $this->db->query($sql);
958 if (!$resql) {
959 $this->error = $this->db->lasterror();
960 }
961 }
962 } catch (Exception $e) {
963 $this->error = $e->getMessage();
964 dol_syslog($this->error, LOG_WARNING);
965 }
966 }
967 }
968 } else {
969 dol_print_error($this->db);
970 }
971
972 return $card;
973 }
974
975
986 public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
987 {
988 global $conf;
989 $sepa = null;
990
991 $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
992 $sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
993 $sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
994 $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
995
996 $soc = new Societe($this->db);
997 $soc->fetch($object->fk_soc);
998
999 dol_syslog(get_class($this)."::sepaStripe search stripe ban id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
1000 $resql = $this->db->query($sql);
1001 if ($resql) {
1002 $num = $this->db->num_rows($resql);
1003 if ($num) {
1004 $obj = $this->db->fetch_object($resql);
1005 $cardref = $obj->stripe_card_ref;
1006 dol_syslog(get_class($this)."::sepaStripe paymentmode=".$cardref);
1007 if ($cardref) {
1008 try {
1009 if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage
1010 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
1011 $sepa = $cu->sources->retrieve($cardref);
1012 } else {
1013 $sepa = \Stripe\PaymentMethod::retrieve($cardref);
1014 }
1015 } else {
1016 if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) {
1017 //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided
1018 $sepa = $cu->sources->retrieve($cardref);
1019 } else {
1020 //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works
1021 $sepa = \Stripe\PaymentMethod::retrieve($cardref);
1022 }
1023 }
1024 } catch (Exception $e) {
1025 $this->error = $e->getMessage();
1026 dol_syslog($this->error, LOG_WARNING);
1027 }
1028 } elseif ($createifnotlinkedtostripe) {
1029 $iban = $obj->iban;
1030 $ipaddress = getUserRemoteIP();
1031 $metadata = array('dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress);
1032 if (is_object($object)) {
1033 $metadata['dol_type'] = $object->element;
1034 $metadata['dol_id'] = $object->id;
1035 $metadata['dol_thirdparty_id'] = $soc->id;
1036 }
1037
1038 $description = 'SEPA for IBAN '.$iban;
1039
1040 $dataforcard = array(
1041 'type' => 'sepa_debit',
1042 "sepa_debit" => array('iban' => $iban),
1043 'billing_details' => array(
1044 'name' => $soc->name,
1045 'email' => !empty($soc->email) ? $soc->email : "",
1046 ),
1047 "metadata" => $metadata
1048 );
1049 // Complete owner name
1050 if (!empty($soc->town)) {
1051 $dataforcard['billing_details']['address']['city'] = $soc->town;
1052 }
1053 if (!empty($soc->country_code)) {
1054 $dataforcard['billing_details']['address']['country'] = $soc->country_code;
1055 }
1056 if (!empty($soc->address)) {
1057 $dataforcard['billing_details']['address']['line1'] = $soc->address;
1058 }
1059 if (!empty($soc->zip)) {
1060 $dataforcard['billing_details']['address']['postal_code'] = $soc->zip;
1061 }
1062 if (!empty($soc->state)) {
1063 $dataforcard['billing_details']['address']['state'] = $soc->state;
1064 }
1065
1066 //$a = \Stripe\Stripe::getApiKey();
1067 //var_dump($a);var_dump($stripeacc);exit;
1068 try {
1069 dol_syslog("Try to create sepa_debit");
1070
1071 $service = 'StripeTest';
1072 $servicestatus = 0;
1073 if (getDolGlobalString('STRIPE_LIVE') && !GETPOST('forcesandbox', 'alpha')) {
1074 $service = 'StripeLive';
1075 $servicestatus = 1;
1076 }
1077 // Force to use the correct API key
1078 global $stripearrayofkeysbyenv;
1079 $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key'];
1080
1081 dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
1082
1083 $s = new \Stripe\StripeClient($stripeacc);
1084
1085 //var_dump($dataforcard);exit;
1086
1087 $sepa = $s->paymentMethods->create($dataforcard);
1088 if (!$sepa) {
1089 $this->error = 'Creation of payment method sepa_debit on Stripe has failed';
1090 } else {
1091 // link customer and src
1092 //$cs = $this->getSetupIntent($description, $soc, $cu, '', $status);
1093 $dataforintent = array(0 => ['description' => $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata' => $metadata);
1094
1095 $cs = $s->setupIntents->create($dataforintent);
1096 //$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]);
1097 $cs = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]);
1098 // note: $cs->mandate contains ID of mandate on Stripe side
1099
1100 if (!$cs) {
1101 $this->error = 'Link SEPA <-> Customer failed';
1102 } else {
1103 dol_syslog("Update the payment mode of the customer");
1104
1105 // print json_encode($sepa);
1106
1107 // Save the Stripe payment mode ID into the Dolibarr database
1108 $sql = "UPDATE ".MAIN_DB_PREFIX."societe_rib";
1109 $sql .= " SET stripe_card_ref = '".$this->db->escape($sepa->id)."',";
1110 $sql .= " card_type = 'sepa_debit',";
1111 $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "',";
1112 $sql .= " ext_payment_site = '".$this->db->escape($service)."'";
1113 if (!empty($cs->mandate)) {
1114 $mandateservice = new \Stripe\Mandate($stripeacc);
1115 $mandate = $mandateservice->retrieve($cs->mandate);
1116 if (is_object($mandate) && is_object($mandate->payment_method_details) && is_object($mandate->payment_method_details->sepa_debit)) {
1117 $refmandate = $mandate->payment_method_details->sepa_debit->reference;
1118 //$urlmandate = $mandate->payment_method_details->sepa_debit->url;
1119 $sql .= ", rum = '".$this->db->escape($refmandate)."'";
1120 }
1121 $sql .= ", comment = '".$this->db->escape($cs->mandate)."'";
1122 $sql .= ", date_rum = '".$this->db->idate(dol_now())."'";
1123 }
1124 $sql .= " WHERE rowid = ".((int) $object->id);
1125 $sql .= " AND type = 'ban'";
1126 $resql = $this->db->query($sql);
1127 if (!$resql) {
1128 $this->error = $this->db->lasterror();
1129 }
1130 }
1131 }
1132 } catch (Exception $e) {
1133 $sepa = null;
1134 $this->error = 'Stripe error: '.$e->getMessage().'. Check the BAN information.';
1135 dol_syslog($this->error, LOG_WARNING); // Error from Stripe, so a warning on Dolibarr
1136 }
1137 }
1138 }
1139 } else {
1140 dol_print_error($this->db);
1141 }
1142
1143 return $sepa;
1144 }
1145
1146
1164 public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
1165 {
1166 global $conf;
1167
1168 $error = 0;
1169
1170 if (empty($status)) {
1171 $service = 'StripeTest';
1172 } else {
1173 $service = 'StripeLive';
1174 }
1175
1176 $sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
1177 $sql .= " FROM ".MAIN_DB_PREFIX."societe_account as sa";
1178 $sql .= " WHERE sa.key_account = '".$this->db->escape($customer)."'";
1179 //$sql.= " AND sa.entity IN (".getEntity('societe').")";
1180 $sql .= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
1181
1182 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1183 $result = $this->db->query($sql);
1184 if ($result) {
1185 if ($this->db->num_rows($result)) {
1186 $obj = $this->db->fetch_object($result);
1187 $key = $obj->fk_soc;
1188 } else {
1189 $key = null;
1190 }
1191 } else {
1192 $key = null;
1193 }
1194
1195 $arrayzerounitcurrency = array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
1196 if (!in_array($currency, $arrayzerounitcurrency)) {
1197 $stripeamount = $amount * 100;
1198 } else {
1199 $stripeamount = $amount;
1200 }
1201
1202 $societe = new Societe($this->db);
1203 if ($key > 0) {
1204 $societe->fetch($key);
1205 }
1206
1207 $description = "";
1208 $ref = "";
1209 if ($origin == 'order') {
1210 $order = new Commande($this->db);
1211 $order->fetch($item);
1212 $ref = $order->ref;
1213 $description = "ORD=".$ref.".CUS=".$societe->id.".PM=stripe";
1214 } elseif ($origin == 'invoice') {
1215 $invoice = new Facture($this->db);
1216 $invoice->fetch($item);
1217 $ref = $invoice->ref;
1218 $description = "INV=".$ref.".CUS=".$societe->id.".PM=stripe";
1219 }
1220
1221 $ipaddress = getUserRemoteIP();
1222
1223 $metadata = array(
1224 "dol_id" => (string) $item,
1225 "dol_type" => (string) $origin,
1226 "dol_thirdparty_id" => (string) $societe->id,
1227 'dol_thirdparty_name' => $societe->name,
1228 'dol_version' => DOL_VERSION,
1229 'dol_entity' => $conf->entity,
1230 'ipaddress' => $ipaddress
1231 );
1232 $return = new Stripe($this->db);
1233 try {
1234 // Force to use the correct API key
1235 global $stripearrayofkeysbyenv;
1236 \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
1237
1238 if (empty($conf->stripeconnect->enabled)) { // With a common Stripe account
1239 if (preg_match('/pm_/i', $source)) {
1240 $stripecard = $source;
1241 $amountstripe = $stripeamount;
1242 $FULLTAG = 'PFBO'; // Payment From Back Office
1243 $stripe = $return;
1244 $amounttopay = $amount;
1245 $servicestatus = $status;
1246
1247 dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1248 $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1249
1250 dol_syslog("* createPaymentStripe Create payment for customer ".$customer->id." on source card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1251
1252 // Create payment intent and charge payment (confirmnow = true)
1253 $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1254
1255 $charge = new stdClass();
1256 if ($paymentintent->status == 'succeeded') {
1257 $charge->status = 'ok';
1258 } else {
1259 $charge->status = 'failed';
1260 $charge->failure_code = $stripe->code;
1261 $charge->failure_message = $stripe->error;
1262 $charge->failure_declinecode = $stripe->declinecode;
1263 $stripefailurecode = $stripe->code;
1264 $stripefailuremessage = $stripe->error;
1265 $stripefailuredeclinecode = $stripe->declinecode;
1266 }
1267 } elseif (preg_match('/acct_/i', $source)) {
1268 $charge = \Stripe\Charge::create(array(
1269 "amount" => "$stripeamount",
1270 "currency" => "$currency",
1271 "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1272 "description" => "Stripe payment: ".$description,
1273 "capture" => $capture,
1274 "metadata" => $metadata,
1275 "source" => "$source"
1276 ));
1277 } else {
1278 $paymentarray = array(
1279 "amount" => "$stripeamount",
1280 "currency" => "$currency",
1281 "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1282 "description" => "Stripe payment: ".$description,
1283 "capture" => $capture,
1284 "metadata" => $metadata,
1285 "source" => "$source",
1286 "customer" => "$customer"
1287 );
1288
1289 if ($societe->email && $usethirdpartyemailforreceiptemail) {
1290 $paymentarray["receipt_email"] = $societe->email;
1291 }
1292
1293 $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
1294 }
1295 } else {
1296 // With Stripe Connect
1297 $fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
1298 if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1299 $fee = getDolGlobalString('STRIPE_APPLICATION_FEE_MAXIMAL');
1300 } elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
1301 $fee = getDolGlobalString('STRIPE_APPLICATION_FEE_MINIMAL');
1302 }
1303
1304 if (!in_array($currency, $arrayzerounitcurrency)) {
1305 $stripefee = round($fee * 100);
1306 } else {
1307 $stripefee = round($fee);
1308 }
1309
1310 $paymentarray = array(
1311 "amount" => "$stripeamount",
1312 "currency" => "$currency",
1313 "statement_descriptor_suffix" => dol_trunc($description, 10, 'right', 'UTF-8', 1), // 22 chars that appears on bank receipt (company + description)
1314 "description" => "Stripe payment: ".$description,
1315 "capture" => $capture,
1316 "metadata" => $metadata,
1317 "source" => "$source",
1318 "customer" => "$customer"
1319 );
1320 if ($conf->entity != $conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0) {
1321 $paymentarray["application_fee_amount"] = $stripefee;
1322 }
1323 if ($societe->email && $usethirdpartyemailforreceiptemail) {
1324 $paymentarray["receipt_email"] = $societe->email;
1325 }
1326
1327 if (preg_match('/pm_/i', $source)) {
1328 $stripecard = $source;
1329 $amountstripe = $stripeamount;
1330 $FULLTAG = 'PFBO'; // Payment From Back Office
1331 $stripe = $return;
1332 $amounttopay = $amount;
1333 $servicestatus = $status;
1334
1335 dol_syslog("* createPaymentStripe get stripeacc", LOG_DEBUG);
1336 $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account if it exists (no network access here)
1337
1338 dol_syslog("* createPaymentStripe Create payment on card ".$stripecard->id.", amounttopay=".$amounttopay.", amountstripe=".$amountstripe.", FULLTAG=".$FULLTAG, LOG_DEBUG);
1339
1340 // Create payment intent and charge payment (confirmnow = true)
1341 $paymentintent = $stripe->getPaymentIntent($amounttopay, $currency, $FULLTAG, $description, $invoice, $customer->id, $stripeacc, $servicestatus, 0, 'automatic', true, $stripecard->id, 1);
1342
1343 $charge = new stdClass();
1344 if ($paymentintent->status == 'succeeded') {
1345 $charge->status = 'ok';
1346 $charge->id = $paymentintent->id;
1347 } else {
1348 $charge->status = 'failed';
1349 $charge->failure_code = $stripe->code;
1350 $charge->failure_message = $stripe->error;
1351 $charge->failure_declinecode = $stripe->declinecode;
1352 }
1353 } else {
1354 $charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
1355 }
1356 }
1357 if (isset($charge->id)) {
1358 }
1359
1360 $return->result = 'success';
1361 $return->id = $charge->id;
1362
1363 if (preg_match('/pm_/i', $source)) {
1364 $return->message = 'Payment retrieved by card status = '.$charge->status;
1365 } else {
1366 if ($charge->source->type == 'card') {
1367 $return->message = $charge->source->card->brand." ....".$charge->source->card->last4;
1368 } elseif ($charge->source->type == 'three_d_secure') {
1369 $stripe = new Stripe($this->db);
1370 $src = \Stripe\Source::retrieve("".$charge->source->three_d_secure->card, array(
1371 "stripe_account" => $stripe->getStripeAccount($service)
1372 ));
1373 $return->message = $src->card->brand." ....".$src->card->last4;
1374 } else {
1375 $return->message = $charge->id;
1376 }
1377 }
1378 } catch (\Stripe\Exception\CardException $e) {
1379 include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
1380 // Since it's a decline, \Stripe\Exception\Card will be caught
1381 $body = $e->getJsonBody();
1382 $err = $body['error'];
1383
1384 $return->result = 'error';
1385 $return->id = $err['charge'];
1386 $return->type = $err['type'];
1387 $return->code = $err['code'];
1388 $return->message = $err['message'];
1389 $body = "Error: <br>".$return->id." ".$return->message." ";
1390 $subject = '[Alert] Payment error using Stripe';
1391 $cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
1392 $cmailfile->sendfile();
1393
1394 $error++;
1395 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1396 } catch (\Stripe\Exception\RateLimitException $e) {
1397 // Too many requests made to the API too quickly
1398 $error++;
1399 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1400 } catch (\Stripe\Exception\InvalidRequestException $e) {
1401 // Invalid parameters were supplied to Stripe's API
1402 $error++;
1403 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1404 } catch (\Stripe\Exception\AuthenticationException $e) {
1405 // Authentication with Stripe's API failed
1406 // (maybe you changed API keys recently)
1407 $error++;
1408 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1409 } catch (\Stripe\Exception\ApiConnectionException $e) {
1410 // Network communication with Stripe failed
1411 $error++;
1412 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1413 } catch (\Stripe\Exception\ExceptionInterface $e) {
1414 // Display a very generic error to the user, and maybe send
1415 // yourself an email
1416 $error++;
1417 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1418 } catch (Exception $e) {
1419 // Something else happened, completely unrelated to Stripe
1420 $error++;
1421 dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
1422 }
1423 return $return;
1424 }
1425}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
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 @TODO No reason to extends CommonObject.
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 (create it if it doesn't exists and $createifnotlinkedt...
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
customerStripe(CommonObject $object, $key='', $status=0, $createifnotlinkedtostripe=0)
Get the Stripe customer of a thirdparty (with option to create it in Stripe if not linked yet).
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.
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)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
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 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.