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