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