| Total Complexity | 190 |
| Total Lines | 1420 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like Stripe often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Stripe, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 34 | class Stripe extends GenericDocument |
||
| 35 | { |
||
| 36 | /** |
||
| 37 | * @var int ID |
||
| 38 | */ |
||
| 39 | public $rowid; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var int Thirdparty ID |
||
| 43 | */ |
||
| 44 | public $fk_soc; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * @var int ID |
||
| 48 | */ |
||
| 49 | public $fk_key; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * @var int ID |
||
| 53 | */ |
||
| 54 | public $id; |
||
| 55 | |||
| 56 | /** |
||
| 57 | * @var string |
||
| 58 | */ |
||
| 59 | public $mode; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @var int Entity |
||
| 63 | */ |
||
| 64 | public $entity; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * @var string |
||
| 68 | */ |
||
| 69 | public $statut; |
||
| 70 | |||
| 71 | public $type; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * @var string |
||
| 75 | */ |
||
| 76 | public $code; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * @var string |
||
| 80 | */ |
||
| 81 | public $declinecode; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @var string Message |
||
| 85 | */ |
||
| 86 | public $message; |
||
| 87 | |||
| 88 | /** |
||
| 89 | * Constructor |
||
| 90 | * |
||
| 91 | * @param DoliDB $db Database handler |
||
|
|
|||
| 92 | */ |
||
| 93 | public function __construct($db) |
||
| 94 | { |
||
| 95 | $this->db = $db; |
||
| 96 | } |
||
| 97 | |||
| 98 | /** |
||
| 99 | * getStripeCustomerAccount |
||
| 100 | * |
||
| 101 | * @param int $id Id of third party |
||
| 102 | * @param int $status Status |
||
| 103 | * @param string $site_account Value to use to identify with account to use on site when site can offer several |
||
| 104 | * accounts. For example: 'pk_live_123456' when using Stripe service. |
||
| 105 | * |
||
| 106 | * @return string Stripe customer ref 'cu_xxxxxxxxxxxxx' or '' |
||
| 107 | */ |
||
| 108 | public function getStripeCustomerAccount($id, $status = 0, $site_account = '') |
||
| 109 | { |
||
| 110 | include_once DOL_DOCUMENT_ROOT . '/societe/class/societeaccount.class.php'; |
||
| 111 | $societeaccount = new SocieteAccount($this->db); |
||
| 112 | return $societeaccount->getCustomerAccount($id, 'stripe', $status, $site_account); // Get thirdparty cus_... |
||
| 113 | } |
||
| 114 | |||
| 115 | /** |
||
| 116 | * Get the Stripe customer of a thirdparty (with option to create it in Stripe if not linked yet). |
||
| 117 | * Search on site_account = 0 or = $stripearrayofkeysbyenv[$status]['publishable_key'] |
||
| 118 | * |
||
| 119 | * @param Societe $object Object thirdparty to check, or create on stripe (create on stripe also |
||
| 120 | * update the stripe_account table for current entity) |
||
| 121 | * @param string $key ''=Use common API. If not '', it is the Stripe connect account |
||
| 122 | * 'acc_....' to use Stripe connect |
||
| 123 | * @param int $status Status (0=test, 1=live) |
||
| 124 | * @param int $createifnotlinkedtostripe 1=Create the stripe customer and the link if the thirdparty is not yet |
||
| 125 | * linked to a stripe customer |
||
| 126 | * |
||
| 127 | * @return \Stripe\Customer|null Stripe Customer or null if not found |
||
| 128 | */ |
||
| 129 | public function customerStripe(Company $object, $key = '', $status = 0, $createifnotlinkedtostripe = 0) |
||
| 236 | } |
||
| 237 | |||
| 238 | /** |
||
| 239 | * Get the Stripe payment method Object from its ID |
||
| 240 | * |
||
| 241 | * @param string $paymentmethod Payment Method ID |
||
| 242 | * @param string $key ''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use |
||
| 243 | * Stripe connect |
||
| 244 | * @param int $status Status (0=test, 1=live) |
||
| 245 | * |
||
| 246 | * @return \Stripe\PaymentMethod|null Stripe PaymentMethod or null if not found |
||
| 247 | */ |
||
| 248 | public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0) |
||
| 249 | { |
||
| 250 | $stripepaymentmethod = null; |
||
| 251 | |||
| 252 | try { |
||
| 253 | // Force to use the correct API key |
||
| 254 | global $stripearrayofkeysbyenv; |
||
| 255 | \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); |
||
| 256 | if (empty($key)) { // If the Stripe connect account not set, we use common API usage |
||
| 257 | $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id); |
||
| 258 | } else { |
||
| 259 | $stripepaymentmethod = \Stripe\PaymentMethod::retrieve((string) $paymentmethod->id, ["stripe_account" => $key]); |
||
| 260 | } |
||
| 261 | } catch (Exception $e) { |
||
| 262 | $this->error = $e->getMessage(); |
||
| 263 | } |
||
| 264 | |||
| 265 | return $stripepaymentmethod; |
||
| 266 | } |
||
| 267 | |||
| 268 | /** |
||
| 269 | * Get the Stripe reader Object from its ID |
||
| 270 | * |
||
| 271 | * @param string $reader Reader ID |
||
| 272 | * @param string $key ''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe |
||
| 273 | * connect |
||
| 274 | * @param int $status Status (0=test, 1=live) |
||
| 275 | * |
||
| 276 | * @return \Stripe\Terminal\Reader|null Stripe Reader or null if not found |
||
| 277 | */ |
||
| 278 | public function getSelectedReader($reader, $key = '', $status = 0) |
||
| 279 | { |
||
| 280 | $selectedreader = null; |
||
| 281 | |||
| 282 | try { |
||
| 283 | // Force to use the correct API key |
||
| 284 | global $stripearrayofkeysbyenv; |
||
| 285 | \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); |
||
| 286 | if (empty($key)) { // If the Stripe connect account not set, we use common API usage |
||
| 287 | $selectedreader = \Stripe\Terminal\Reader::retrieve((string) $reader); |
||
| 288 | } else { |
||
| 289 | $stripepaymentmethod = \Stripe\Terminal\Reader::retrieve((string) $reader, ["stripe_account" => $key]); |
||
| 290 | } |
||
| 291 | } catch (Exception $e) { |
||
| 292 | $this->error = $e->getMessage(); |
||
| 293 | } |
||
| 294 | |||
| 295 | return $selectedreader; |
||
| 296 | } |
||
| 297 | |||
| 298 | /** |
||
| 299 | * Get the Stripe payment intent. Create it with confirmnow=false |
||
| 300 | * Warning. If a payment was tried and failed, a payment intent was created. |
||
| 301 | * But if we change something on object to pay (amount or other), reusing same payment intent is not allowed. |
||
| 302 | * Recommended solution is to recreate a new payment intent each time we need one (old one will be automatically |
||
| 303 | * closed after a delay), that's why i comment the part of code to retrieve a payment intent with object id (never |
||
| 304 | * mind if we cumulate payment intent with old ones that will not be used) Note: This is used when option |
||
| 305 | * STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION is on when making a payment from the public/payment/newpayment.php |
||
| 306 | * page but not when using the STRIPE_USE_NEW_CHECKOUT. |
||
| 307 | * |
||
| 308 | * @param string $description Description |
||
| 309 | * @param Societe $object Object of company to link the Stripe payment mode with |
||
| 310 | * @param string $customer Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe() |
||
| 311 | * @param string $key ''=Use common API. If not '', it is the Stripe connect account |
||
| 312 | * 'acc_....' to use Stripe connect |
||
| 313 | * @param int $status Status (0=test, 1=live) |
||
| 314 | * @param int $usethirdpartyemailforreceiptemail 1=use thirdparty email for receipt |
||
| 315 | * @param boolean $confirmnow false=default, true=try to confirm immediately after create |
||
| 316 | * (if conditions are ok) |
||
| 317 | * |
||
| 318 | * @return \Stripe\SetupIntent|null Stripe SetupIntent or null if not found and failed to |
||
| 319 | * create |
||
| 320 | */ |
||
| 321 | public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false) |
||
| 322 | { |
||
| 323 | global $conf; |
||
| 324 | |||
| 325 | dol_syslog("getSetupIntent description=" . $description . ' confirmnow=' . $confirmnow, LOG_INFO, 1); |
||
| 326 | |||
| 327 | $error = 0; |
||
| 328 | |||
| 329 | if (empty($status)) { |
||
| 330 | $service = 'StripeTest'; |
||
| 331 | } else { |
||
| 332 | $service = 'StripeLive'; |
||
| 333 | } |
||
| 334 | |||
| 335 | $setupintent = null; |
||
| 336 | |||
| 337 | if (empty($setupintent)) { |
||
| 338 | $ipaddress = getUserRemoteIP(); |
||
| 339 | $metadata = ['dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress]; |
||
| 340 | if (is_object($object)) { |
||
| 341 | $metadata['dol_type'] = $object->element; |
||
| 342 | $metadata['dol_id'] = $object->id; |
||
| 343 | if (is_object($object->thirdparty) && $object->thirdparty->id > 0) { |
||
| 344 | $metadata['dol_thirdparty_id'] = $object->thirdparty->id; |
||
| 345 | } |
||
| 346 | } |
||
| 347 | |||
| 348 | // list of payment method types |
||
| 349 | $paymentmethodtypes = ["card"]; |
||
| 350 | if (getDolGlobalString('STRIPE_SEPA_DIRECT_DEBIT')) { |
||
| 351 | $paymentmethodtypes[] = "sepa_debit"; //&& ($object->thirdparty->isInEEC()) |
||
| 352 | } |
||
| 353 | if (getDolGlobalString('STRIPE_BANCONTACT')) { |
||
| 354 | $paymentmethodtypes[] = "bancontact"; |
||
| 355 | } |
||
| 356 | if (getDolGlobalString('STRIPE_IDEAL')) { |
||
| 357 | $paymentmethodtypes[] = "ideal"; |
||
| 358 | } |
||
| 359 | // Giropay not possible for setup intent |
||
| 360 | if (getDolGlobalString('STRIPE_SOFORT')) { |
||
| 361 | $paymentmethodtypes[] = "sofort"; |
||
| 362 | } |
||
| 363 | |||
| 364 | global $dolibarr_main_url_root; |
||
| 365 | |||
| 366 | $dataforintent = [ |
||
| 367 | "confirm" => $confirmnow, // Do not confirm immediately during creation of intent |
||
| 368 | "payment_method_types" => $paymentmethodtypes, // When payment_method_types is set, return_url is not required but payment mode can't be managed from dashboard |
||
| 369 | /* |
||
| 370 | 'return_url' => $dolibarr_main_url_root.'/public/payment/paymentok.php', |
||
| 371 | 'automatic_payment_methods' => array( |
||
| 372 | 'enabled' => true, |
||
| 373 | 'allow_redirects' => 'never', |
||
| 374 | ), |
||
| 375 | */ |
||
| 376 | "usage" => "off_session", |
||
| 377 | "metadata" => $metadata, |
||
| 378 | ]; |
||
| 379 | if (!is_null($customer)) { |
||
| 380 | $dataforintent["customer"] = $customer; |
||
| 381 | } |
||
| 382 | if (!is_null($description)) { |
||
| 383 | $dataforintent["description"] = $description; |
||
| 384 | } |
||
| 385 | // payment_method = |
||
| 386 | // payment_method_types = array('card') |
||
| 387 | //var_dump($dataforintent); |
||
| 388 | |||
| 389 | if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email) { |
||
| 390 | $dataforintent["receipt_email"] = $object->thirdparty->email; |
||
| 391 | } |
||
| 392 | |||
| 393 | try { |
||
| 394 | // Force to use the correct API key |
||
| 395 | global $stripearrayofkeysbyenv; |
||
| 396 | \Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']); |
||
| 397 | |||
| 398 | dol_syslog("getSetupIntent " . $stripearrayofkeysbyenv[$status]['publishable_key'], LOG_DEBUG); |
||
| 399 | |||
| 400 | // 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. |
||
| 401 | if (empty($key)) { // If the Stripe connect account not set, we use common API usage |
||
| 402 | //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description")); |
||
| 403 | $setupintent = \Stripe\SetupIntent::create($dataforintent, []); |
||
| 404 | } else { |
||
| 405 | //$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key)); |
||
| 406 | $setupintent = \Stripe\SetupIntent::create($dataforintent, ["stripe_account" => $key]); |
||
| 407 | } |
||
| 408 | //var_dump($setupintent->id); |
||
| 409 | |||
| 410 | // Store the setup intent |
||
| 411 | /*if (is_object($object)) |
||
| 412 | { |
||
| 413 | $setupintentalreadyexists = 0; |
||
| 414 | // Check that payment intent $setupintent->id is not already recorded. |
||
| 415 | $sql = "SELECT pi.rowid"; |
||
| 416 | $sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_demande as pi"; |
||
| 417 | $sql.= " WHERE pi.entity IN (".getEntity('societe').")"; |
||
| 418 | $sql.= " AND pi.ext_payment_site = '" . $this->db->escape($service) . "'"; |
||
| 419 | $sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'"; |
||
| 420 | |||
| 421 | dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_demande", LOG_DEBUG); |
||
| 422 | $resql = $this->db->query($sql); |
||
| 423 | if ($resql) { |
||
| 424 | $num = $this->db->num_rows($resql); |
||
| 425 | if ($num) |
||
| 426 | { |
||
| 427 | $obj = $this->db->fetch_object($resql); |
||
| 428 | if ($obj) $setupintentalreadyexists++; |
||
| 429 | } |
||
| 430 | } |
||
| 431 | else dol_print_error($this->db); |
||
| 432 | |||
| 433 | // If not, we create it. |
||
| 434 | if (! $setupintentalreadyexists) |
||
| 435 | { |
||
| 436 | $now=dol_now(); |
||
| 437 | $sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)"; |
||
| 438 | $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).")"; |
||
| 439 | $resql = $this->db->query($sql); |
||
| 440 | if (! $resql) |
||
| 441 | { |
||
| 442 | $error++; |
||
| 443 | $this->error = $this->db->lasterror(); |
||
| 444 | dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database."); |
||
| 445 | } |
||
| 446 | } |
||
| 447 | } |
||
| 448 | else |
||
| 449 | { |
||
| 450 | $_SESSION["stripe_setup_intent"] = $setupintent; |
||
| 451 | }*/ |
||
| 452 | } catch (Exception $e) { |
||
| 453 | //var_dump($dataforintent); |
||
| 454 | //var_dump($description); |
||
| 455 | //var_dump($key); |
||
| 456 | //var_dump($setupintent); |
||
| 457 | //var_dump($e->getMessage()); |
||
| 458 | $error++; |
||
| 459 | $this->error = $e->getMessage(); |
||
| 460 | } |
||
| 461 | } |
||
| 462 | |||
| 463 | if (!$error) { |
||
| 464 | dol_syslog("getSetupIntent " . (is_object($setupintent) ? $setupintent->id : ''), LOG_INFO, -1); |
||
| 465 | return $setupintent; |
||
| 466 | } else { |
||
| 467 | dol_syslog("getSetupIntent return error=" . $error, LOG_INFO, -1); |
||
| 468 | return null; |
||
| 469 | } |
||
| 470 | } |
||
| 471 | |||
| 472 | /** |
||
| 473 | * Get the Stripe card of a company payment mode (option to create it on Stripe if not linked yet is no more |
||
| 474 | * available on new Stripe API) |
||
| 475 | * |
||
| 476 | * @param \Stripe\Customer $cu Object stripe customer. |
||
| 477 | * @param CompanyPaymentMode $object Object companypaymentmode to check, or create on stripe |
||
| 478 | * (create on stripe also update the societe_rib table for |
||
| 479 | * current entity) |
||
| 480 | * @param string $stripeacc ''=Use common API. If not '', it is the Stripe connect |
||
| 481 | * account 'acc_....' to use Stripe connect |
||
| 482 | * @param int $status Status (0=test, 1=live) |
||
| 483 | * @param int $createifnotlinkedtostripe 1=Create the stripe card and the link if the card is not |
||
| 484 | * yet linked to a stripe card. Deprecated with new Stripe API |
||
| 485 | * and SCA. |
||
| 486 | * |
||
| 487 | * @return \Stripe\Card|\Stripe\PaymentMethod|null Stripe Card or null if not found |
||
| 488 | */ |
||
| 489 | public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0) |
||
| 490 | { |
||
| 491 | global $conf, $user, $langs; |
||
| 492 | |||
| 493 | $card = null; |
||
| 494 | |||
| 495 | $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_.... |
||
| 496 | $sql .= " FROM " . MAIN_DB_PREFIX . "societe_rib as sa"; |
||
| 497 | $sql .= " WHERE sa.rowid = " . ((int) $object->id); // We get record from ID, no need for filter on entity |
||
| 498 | $sql .= " AND sa.type = 'card'"; |
||
| 499 | |||
| 500 | dol_syslog(get_class($this) . "::cardStripe search stripe card id for paymentmode id=" . $object->id . ", stripeacc=" . $stripeacc . ", status=" . $status . ", createifnotlinkedtostripe=" . $createifnotlinkedtostripe, LOG_DEBUG); |
||
| 501 | $resql = $this->db->query($sql); |
||
| 502 | if ($resql) { |
||
| 503 | $num = $this->db->num_rows($resql); |
||
| 504 | if ($num) { |
||
| 505 | $obj = $this->db->fetch_object($resql); |
||
| 506 | $cardref = $obj->stripe_card_ref; |
||
| 507 | dol_syslog(get_class($this) . "::cardStripe cardref=" . $cardref); |
||
| 508 | if ($cardref) { |
||
| 509 | try { |
||
| 510 | if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage |
||
| 511 | if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) { |
||
| 512 | $card = $cu->sources->retrieve($cardref); |
||
| 513 | } else { |
||
| 514 | $card = \Stripe\PaymentMethod::retrieve($cardref); |
||
| 515 | } |
||
| 516 | } else { |
||
| 517 | if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) { |
||
| 518 | //$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided |
||
| 519 | $card = $cu->sources->retrieve($cardref); |
||
| 520 | } else { |
||
| 521 | //$card = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works |
||
| 522 | $card = \Stripe\PaymentMethod::retrieve($cardref); |
||
| 523 | } |
||
| 524 | } |
||
| 525 | } catch (Exception $e) { |
||
| 526 | $this->error = $e->getMessage(); |
||
| 527 | dol_syslog($this->error, LOG_WARNING); |
||
| 528 | } |
||
| 529 | } elseif ($createifnotlinkedtostripe) { |
||
| 530 | // Deprecated with new Stripe API and SCA. We should not use anymore this part of code now. |
||
| 531 | $exp_date_month = $obj->exp_date_month; |
||
| 532 | $exp_date_year = $obj->exp_date_year; |
||
| 533 | $number = $obj->number; |
||
| 534 | $cvc = $obj->cvn; // cvn in database, cvc for stripe |
||
| 535 | $cardholdername = $obj->proprio; |
||
| 536 | |||
| 537 | $ipaddress = getUserRemoteIP(); |
||
| 538 | |||
| 539 | $dataforcard = [ |
||
| 540 | "source" => [ |
||
| 541 | 'object' => 'card', |
||
| 542 | 'exp_month' => $exp_date_month, |
||
| 543 | 'exp_year' => $exp_date_year, |
||
| 544 | 'number' => $number, |
||
| 545 | 'cvc' => $cvc, |
||
| 546 | 'name' => $cardholdername, |
||
| 547 | ], |
||
| 548 | "metadata" => [ |
||
| 549 | 'dol_type' => $object->element, |
||
| 550 | 'dol_id' => $object->id, |
||
| 551 | 'dol_version' => DOL_VERSION, |
||
| 552 | 'dol_entity' => $conf->entity, |
||
| 553 | 'ipaddress' => $ipaddress, |
||
| 554 | ], |
||
| 555 | ]; |
||
| 556 | |||
| 557 | //$a = \Stripe\Stripe::getApiKey(); |
||
| 558 | //var_dump($a); |
||
| 559 | //var_dump($stripeacc);exit; |
||
| 560 | try { |
||
| 561 | if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage |
||
| 562 | if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) { |
||
| 563 | dol_syslog("Try to create card with dataforcard = " . json_encode($dataforcard)); |
||
| 564 | $card = $cu->sources->create($dataforcard); |
||
| 565 | if (!$card) { |
||
| 566 | $this->error = 'Creation of card on Stripe has failed'; |
||
| 567 | } |
||
| 568 | } else { |
||
| 569 | $connect = ''; |
||
| 570 | if (!empty($stripeacc)) { |
||
| 571 | $connect = $stripeacc . '/'; |
||
| 572 | } |
||
| 573 | $url = 'https://dashboard.stripe.com/' . $connect . 'test/customers/' . $cu->id; |
||
| 574 | if ($status) { |
||
| 575 | $url = 'https://dashboard.stripe.com/' . $connect . 'customers/' . $cu->id; |
||
| 576 | } |
||
| 577 | $urtoswitchonstripe = '<a href="' . $url . '" target="_stripe">' . img_picto($langs->trans('ShowInStripe'), 'globe') . '</a>'; |
||
| 578 | |||
| 579 | //dol_syslog("Error: This case is not supported", LOG_ERR); |
||
| 580 | $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}')); |
||
| 581 | } |
||
| 582 | } else { |
||
| 583 | if (!getDolGlobalString('STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION')) { |
||
| 584 | dol_syslog("Try to create card with dataforcard = " . json_encode($dataforcard)); |
||
| 585 | $card = $cu->sources->create($dataforcard, ["stripe_account" => $stripeacc]); |
||
| 586 | if (!$card) { |
||
| 587 | $this->error = 'Creation of card on Stripe has failed'; |
||
| 588 | } |
||
| 589 | } else { |
||
| 590 | $connect = ''; |
||
| 591 | if (!empty($stripeacc)) { |
||
| 592 | $connect = $stripeacc . '/'; |
||
| 593 | } |
||
| 594 | $url = 'https://dashboard.stripe.com/' . $connect . 'test/customers/' . $cu->id; |
||
| 595 | if ($status) { |
||
| 596 | $url = 'https://dashboard.stripe.com/' . $connect . 'customers/' . $cu->id; |
||
| 597 | } |
||
| 598 | $urtoswitchonstripe = '<a href="' . $url . '" target="_stripe">' . img_picto($langs->trans('ShowInStripe'), 'globe') . '</a>'; |
||
| 599 | |||
| 600 | //dol_syslog("Error: This case is not supported", LOG_ERR); |
||
| 601 | $this->error = str_replace('{s1}', $urtoswitchonstripe, $langs->trans('CreationOfPaymentModeMustBeDoneFromStripeInterface', '{s1}')); |
||
| 602 | } |
||
| 603 | } |
||
| 604 | |||
| 605 | if ($card) { |
||
| 606 | $sql = "UPDATE " . MAIN_DB_PREFIX . "societe_rib"; |
||
| 607 | $sql .= " SET stripe_card_ref = '" . $this->db->escape($card->id) . "', card_type = '" . $this->db->escape($card->brand) . "',"; |
||
| 608 | $sql .= " country_code = '" . $this->db->escape($card->country) . "',"; |
||
| 609 | $sql .= " approved = " . ($card->cvc_check == 'pass' ? 1 : 0); |
||
| 610 | $sql .= " WHERE rowid = " . ((int) $object->id); |
||
| 611 | $sql .= " AND type = 'card'"; |
||
| 612 | $resql = $this->db->query($sql); |
||
| 613 | if (!$resql) { |
||
| 614 | $this->error = $this->db->lasterror(); |
||
| 615 | } |
||
| 616 | } |
||
| 617 | } catch (Exception $e) { |
||
| 618 | $this->error = $e->getMessage(); |
||
| 619 | dol_syslog($this->error, LOG_WARNING); |
||
| 620 | } |
||
| 621 | } |
||
| 622 | } |
||
| 623 | } else { |
||
| 624 | dol_print_error($this->db); |
||
| 625 | } |
||
| 626 | |||
| 627 | return $card; |
||
| 628 | } |
||
| 629 | |||
| 630 | /** |
||
| 631 | * Get the Stripe SEPA of a company payment mode (create it if it doesn't exists and $createifnotlinkedtostripe is |
||
| 632 | * set) |
||
| 633 | * |
||
| 634 | * @param \Stripe\Customer $cu Object stripe customer. |
||
| 635 | * @param CompanyPaymentMode $object Object companypaymentmode to check, or create on stripe |
||
| 636 | * (create on stripe also update the societe_rib table for |
||
| 637 | * current entity) |
||
| 638 | * @param string $stripeacc ''=Use common API. If not '', it is the Stripe connect |
||
| 639 | * account 'acc_....' to use Stripe connect |
||
| 640 | * @param int $status Status (0=test, 1=live) |
||
| 641 | * @param int $createifnotlinkedtostripe 1=Create the stripe sepa and the link if the sepa is not |
||
| 642 | * yet linked to a stripe sepa. Used by the "Create bank to |
||
| 643 | * Stripe" feature. |
||
| 644 | * |
||
| 645 | * @return \Stripe\PaymentMethod|null Stripe SEPA or null if not found |
||
| 646 | */ |
||
| 647 | public function sepaStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0) |
||
| 648 | { |
||
| 649 | global $conf; |
||
| 650 | $sepa = null; |
||
| 651 | |||
| 652 | $sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA |
||
| 653 | $sql .= " FROM " . MAIN_DB_PREFIX . "societe_rib as sa"; |
||
| 654 | $sql .= " WHERE sa.rowid = " . ((int) $object->id); // We get record from ID, no need for filter on entity |
||
| 655 | $sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement) |
||
| 656 | |||
| 657 | $soc = new Company($this->db); |
||
| 658 | $soc->fetch($object->fk_soc); |
||
| 659 | |||
| 660 | dol_syslog(get_class($this) . "::sepaStripe search stripe ban id for paymentmode id=" . $object->id . ", stripeacc=" . $stripeacc . ", status=" . $status . ", createifnotlinkedtostripe=" . $createifnotlinkedtostripe, LOG_DEBUG); |
||
| 661 | $resql = $this->db->query($sql); |
||
| 662 | if ($resql) { |
||
| 663 | $num = $this->db->num_rows($resql); |
||
| 664 | if ($num) { |
||
| 665 | $obj = $this->db->fetch_object($resql); |
||
| 666 | $cardref = $obj->stripe_card_ref; |
||
| 667 | dol_syslog(get_class($this) . "::sepaStripe paymentmode=" . $cardref); |
||
| 668 | if ($cardref) { |
||
| 669 | try { |
||
| 670 | if (empty($stripeacc)) { // If the Stripe connect account not set, we use common API usage |
||
| 671 | if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) { |
||
| 672 | $sepa = $cu->sources->retrieve($cardref); |
||
| 673 | } else { |
||
| 674 | $sepa = \Stripe\PaymentMethod::retrieve($cardref); |
||
| 675 | } |
||
| 676 | } else { |
||
| 677 | if (!preg_match('/^pm_/', $cardref) && !empty($cu->sources)) { |
||
| 678 | //$sepa = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc)); // this API fails when array stripe_account is provided |
||
| 679 | $sepa = $cu->sources->retrieve($cardref); |
||
| 680 | } else { |
||
| 681 | //$sepa = \Stripe\PaymentMethod::retrieve($cardref, array("stripe_account" => $stripeacc)); // Don't know if this works |
||
| 682 | $sepa = \Stripe\PaymentMethod::retrieve($cardref); |
||
| 683 | } |
||
| 684 | } |
||
| 685 | } catch (Exception $e) { |
||
| 686 | $this->error = $e->getMessage(); |
||
| 687 | dol_syslog($this->error, LOG_WARNING); |
||
| 688 | } |
||
| 689 | } elseif ($createifnotlinkedtostripe) { |
||
| 690 | $iban = $obj->iban; |
||
| 691 | $ipaddress = getUserRemoteIP(); |
||
| 692 | $metadata = ['dol_version' => DOL_VERSION, 'dol_entity' => $conf->entity, 'ipaddress' => $ipaddress]; |
||
| 693 | if (is_object($object)) { |
||
| 694 | $metadata['dol_type'] = $object->element; |
||
| 695 | $metadata['dol_id'] = $object->id; |
||
| 696 | $metadata['dol_thirdparty_id'] = $soc->id; |
||
| 697 | } |
||
| 698 | |||
| 699 | $description = 'SEPA for IBAN ' . $iban; |
||
| 700 | |||
| 701 | $dataforcard = [ |
||
| 702 | 'type' => 'sepa_debit', |
||
| 703 | "sepa_debit" => ['iban' => $iban], |
||
| 704 | 'billing_details' => [ |
||
| 705 | 'name' => $soc->name, |
||
| 706 | 'email' => !empty($soc->email) ? $soc->email : "", |
||
| 707 | ], |
||
| 708 | "metadata" => $metadata, |
||
| 709 | ]; |
||
| 710 | // Complete owner name |
||
| 711 | if (!empty($soc->town)) { |
||
| 712 | $dataforcard['billing_details']['address']['city'] = $soc->town; |
||
| 713 | } |
||
| 714 | if (!empty($soc->country_code)) { |
||
| 715 | $dataforcard['billing_details']['address']['country'] = $soc->country_code; |
||
| 716 | } |
||
| 717 | if (!empty($soc->address)) { |
||
| 718 | $dataforcard['billing_details']['address']['line1'] = $soc->address; |
||
| 719 | } |
||
| 720 | if (!empty($soc->zip)) { |
||
| 721 | $dataforcard['billing_details']['address']['postal_code'] = $soc->zip; |
||
| 722 | } |
||
| 723 | if (!empty($soc->state)) { |
||
| 724 | $dataforcard['billing_details']['address']['state'] = $soc->state; |
||
| 725 | } |
||
| 726 | |||
| 727 | //$a = \Stripe\Stripe::getApiKey(); |
||
| 728 | //var_dump($a);var_dump($stripeacc);exit; |
||
| 729 | try { |
||
| 730 | dol_syslog("Try to create sepa_debit"); |
||
| 731 | |||
| 732 | $service = 'StripeTest'; |
||
| 733 | $servicestatus = 0; |
||
| 734 | if (getDolGlobalString('STRIPE_LIVE') && !GETPOST('forcesandbox', 'alpha')) { |
||
| 735 | $service = 'StripeLive'; |
||
| 736 | $servicestatus = 1; |
||
| 737 | } |
||
| 738 | // Force to use the correct API key |
||
| 739 | global $stripearrayofkeysbyenv; |
||
| 740 | $stripeacc = $stripearrayofkeysbyenv[$servicestatus]['secret_key']; |
||
| 741 | |||
| 742 | dol_syslog("Try to create sepa_debit with data = " . json_encode($dataforcard)); |
||
| 743 | |||
| 744 | $s = new \Stripe\StripeClient($stripeacc); |
||
| 745 | |||
| 746 | //var_dump($dataforcard);exit; |
||
| 747 | |||
| 748 | $sepa = $s->paymentMethods->create($dataforcard); |
||
| 749 | if (!$sepa) { |
||
| 750 | $this->error = 'Creation of payment method sepa_debit on Stripe has failed'; |
||
| 751 | } else { |
||
| 752 | // link customer and src |
||
| 753 | //$cs = $this->getSetupIntent($description, $soc, $cu, '', $status); |
||
| 754 | $dataforintent = [0 => ['description' => $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata' => $metadata]; |
||
| 755 | |||
| 756 | $cs = $s->setupIntents->create($dataforintent); |
||
| 757 | //$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]); |
||
| 758 | $cs = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]); |
||
| 759 | // note: $cs->mandate contains ID of mandate on Stripe side |
||
| 760 | |||
| 761 | if (!$cs) { |
||
| 762 | $this->error = 'Link SEPA <-> Customer failed'; |
||
| 763 | } else { |
||
| 764 | dol_syslog("Update the payment mode of the customer"); |
||
| 765 | |||
| 766 | // print json_encode($sepa); |
||
| 767 | |||
| 768 | // Save the Stripe payment mode ID into the Dolibarr database |
||
| 769 | $sql = "UPDATE " . MAIN_DB_PREFIX . "societe_rib"; |
||
| 770 | $sql .= " SET stripe_card_ref = '" . $this->db->escape($sepa->id) . "',"; |
||
| 771 | $sql .= " card_type = 'sepa_debit',"; |
||
| 772 | $sql .= " stripe_account= '" . $this->db->escape($cu->id . "@" . $stripeacc) . "',"; |
||
| 773 | $sql .= " ext_payment_site = '" . $this->db->escape($service) . "'"; |
||
| 774 | if (!empty($cs->mandate)) { |
||
| 775 | $mandateservice = new \Stripe\Mandate($stripeacc); |
||
| 776 | $mandate = $mandateservice->retrieve($cs->mandate); |
||
| 777 | if (is_object($mandate) && is_object($mandate->payment_method_details) && is_object($mandate->payment_method_details->sepa_debit)) { |
||
| 778 | $refmandate = $mandate->payment_method_details->sepa_debit->reference; |
||
| 779 | //$urlmandate = $mandate->payment_method_details->sepa_debit->url; |
||
| 780 | $sql .= ", rum = '" . $this->db->escape($refmandate) . "'"; |
||
| 781 | } |
||
| 782 | $sql .= ", comment = '" . $this->db->escape($cs->mandate) . "'"; |
||
| 783 | $sql .= ", date_rum = '" . $this->db->idate(dol_now()) . "'"; |
||
| 784 | } |
||
| 785 | $sql .= " WHERE rowid = " . ((int) $object->id); |
||
| 786 | $sql .= " AND type = 'ban'"; |
||
| 787 | $resql = $this->db->query($sql); |
||
| 788 | if (!$resql) { |
||
| 789 | $this->error = $this->db->lasterror(); |
||
| 790 | } |
||
| 791 | } |
||
| 792 | } |
||
| 793 | } catch (Exception $e) { |
||
| 794 | $sepa = null; |
||
| 795 | $this->error = 'Stripe error: ' . $e->getMessage() . '. Check the BAN information.'; |
||
| 796 | dol_syslog($this->error, LOG_WARNING); // Error from Stripe, so a warning on Dolibarr |
||
| 797 | } |
||
| 798 | } |
||
| 799 | } |
||
| 800 | } else { |
||
| 801 | dol_print_error($this->db); |
||
| 802 | } |
||
| 803 | |||
| 804 | return $sepa; |
||
| 805 | } |
||
| 806 | |||
| 807 | /** |
||
| 808 | * Create charge. |
||
| 809 | * This is called by page htdocs/stripe/payment.php and may be deprecated. |
||
| 810 | * |
||
| 811 | * @param int $amount Amount to pay |
||
| 812 | * @param string $currency EUR, GPB... |
||
| 813 | * @param string $origin Object type to pay (order, invoice, contract...) |
||
| 814 | * @param int $item Object id to pay |
||
| 815 | * @param string $source src_xxxxx or card_xxxxx or pm_xxxxx |
||
| 816 | * @param string $customer Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe() |
||
| 817 | * @param string $account Stripe account ref 'acc_xxxxxxxxxxxxx' via getStripeAccount() |
||
| 818 | * @param int $status Status (0=test, 1=live) |
||
| 819 | * @param int $usethirdpartyemailforreceiptemail Use thirdparty email as receipt email |
||
| 820 | * @param boolean $capture Set capture flag to true (take payment) or false (wait) |
||
| 821 | * |
||
| 822 | * @return Stripe |
||
| 823 | */ |
||
| 824 | public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true) |
||
| 1084 | } |
||
| 1085 | |||
| 1086 | /** |
||
| 1087 | * Return main company OAuth Connect stripe account |
||
| 1088 | * |
||
| 1089 | * @param string $mode 'StripeTest' or 'StripeLive' |
||
| 1090 | * @param int $fk_soc Id of thirdparty |
||
| 1091 | * @param int $entity Id of entity (-1 = current environment) |
||
| 1092 | * |
||
| 1093 | * @return string Stripe account 'acc_....' or '' if no OAuth token found |
||
| 1094 | */ |
||
| 1095 | public function getStripeAccount($mode = 'StripeTest', $fk_soc = 0, $entity = -1) |
||
| 1137 | } |
||
| 1138 | |||
| 1139 | /** |
||
| 1140 | * Get the Stripe payment intent. Create it with confirmnow=false |
||
| 1141 | * Warning. If a payment was tried and failed, a payment intent was created. |
||
| 1142 | * But if we change something on object to pay (amount or other), reusing same payment intent is not allowed by |
||
| 1143 | * Stripe. Recommended solution is to recreate a new payment intent each time we need one (old one will be |
||
| 1144 | * automatically closed after a delay), that's why i comment the part of code to retrieve a payment intent with |
||
| 1145 | * object id (never mind if we cumulate payment intent with old ones that will not be used) Note: This is used when |
||
| 1146 | * option STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION is on when making a payment from the |
||
| 1147 | * public/payment/newpayment.php page but not when using the STRIPE_USE_NEW_CHECKOUT. |
||
| 1148 | * |
||
| 1149 | * @param double $amount Amount |
||
| 1150 | * @param string $currency_code Currency code |
||
| 1151 | * @param string $tag Tag |
||
| 1152 | * @param string $description Description |
||
| 1153 | * @param mixed $object Object to pay with Stripe |
||
| 1154 | * @param string $customer Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe() |
||
| 1155 | * @param string $key ''=Use common API. If not '', it is the Stripe connect account |
||
| 1156 | * 'acc_....' to use Stripe connect |
||
| 1157 | * @param int $status Status (0=test, 1=live) |
||
| 1158 | * @param int $usethirdpartyemailforreceiptemail 1=use thirdparty email for receipt |
||
| 1159 | * @param string $mode automatic=automatic confirmation/payment when conditions are |
||
| 1160 | * ok, manual=need to call confirm() on intent |
||
| 1161 | * @param boolean $confirmnow false=default, true=try to confirm immediately after create |
||
| 1162 | * (if conditions are ok) |
||
| 1163 | * @param string $payment_method 'pm_....' (if known) |
||
| 1164 | * @param int $off_session If we use an already known payment method to pay when customer |
||
| 1165 | * is not available during the checkout flow. |
||
| 1166 | * @param int $noidempotency_key Do not use the idempotency_key when creating the PaymentIntent |
||
| 1167 | * @param int $did ID of an existing line into llx_prelevement_demande (Dolibarr |
||
| 1168 | * intent). If provided, no new line will be created. |
||
| 1169 | * |
||
| 1170 | * @return \Stripe\PaymentIntent|null Stripe PaymentIntent or null if not found and failed to |
||
| 1171 | * create |
||
| 1172 | */ |
||
| 1173 | 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) |
||
| 1457 |