Passed
Branch develop (4a815d)
by
unknown
26:55
created

Stripe::getSetupIntent()   C

Complexity

Conditions 13
Paths 244

Size

Total Lines 120
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 37
nc 244
nop 7
dl 0
loc 120
rs 5.2333
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* Copyright (C) 2018-2019 	Thibault FOUCART       <[email protected]>
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 <http://www.gnu.org/licenses/>.
16
 */
17
18
// Put here all includes required by your class file
19
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
20
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
21
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
22
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
23
require_once DOL_DOCUMENT_ROOT.'/stripe/config.php';						// This set stripe global env
24
25
26
/**
27
 *	Stripe class
28
 */
29
class Stripe extends CommonObject
30
{
31
	/**
32
	 * @var int ID
33
	 */
34
	public $rowid;
35
36
	/**
37
	 * @var int Thirdparty ID
38
	 */
39
    public $fk_soc;
40
41
    /**
42
     * @var int ID
43
     */
44
	public $fk_key;
45
46
	/**
47
	 * @var int ID
48
	 */
49
	public $id;
50
51
	public $mode;
52
53
	/**
54
	 * @var int Entity
55
	 */
56
	public $entity;
57
58
	public $statut;
59
60
	public $type;
61
62
	public $code;
63
64
	public $message;
65
66
	/**
67
	 * 	Constructor
68
	 *
69
	 * 	@param	DoliDB		$db			Database handler
70
	 */
71
	public function __construct($db)
72
	{
73
		$this->db = $db;
74
	}
75
76
77
	/**
78
	 * Return main company OAuth Connect stripe account
79
	 *
80
	 * @param 	string	$mode		'StripeTest' or 'StripeLive'
81
	 * @param	int		$fk_soc		Id of thirdparty
82
	 * @return 	string				Stripe account 'acc_....' or '' if no OAuth token found
83
	 */
84
	public function getStripeAccount($mode = 'StripeTest', $fk_soc = 0)
85
	{
86
		global $conf;
87
88
		$sql = "SELECT tokenstring";
89
		$sql.= " FROM ".MAIN_DB_PREFIX."oauth_token";
90
		$sql.= " WHERE entity = ".$conf->entity;
91
		$sql.= " AND service = '".$mode."'";
92
		if ($fk_soc > 0) {
93
			$sql.= " AND fk_soc = ".$fk_soc;
94
		}
95
		else {
96
			$sql.= " AND fk_soc IS NULL";
97
		}
98
		$sql.= " AND fk_user IS NULL AND fk_adherent IS NULL";
99
100
		dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
101
		$result = $this->db->query($sql);
102
    	if ($result) {
103
			if ($this->db->num_rows($result)) {
104
				$obj = $this->db->fetch_object($result);
105
    			$tokenstring=$obj->tokenstring;
106
107
    			$tmparray = dol_json_decode($tokenstring);
108
    			$key = $tmparray->stripe_user_id;
109
    		} else {
110
    			$tokenstring='';
111
    		}
112
    	}
113
    	else {
114
    		dol_print_error($this->db);
115
    	}
116
117
    	dol_syslog("No dedicated Stripe Connect account available for entity ".$conf->entity);
118
		return $key;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $key does not seem to be defined for all execution paths leading up to this point.
Loading history...
119
	}
120
121
	/**
122
	 * getStripeCustomerAccount
123
	 *
124
	 * @param	int		$id		Id of third party
125
	 * @param	int		$status		Status
126
	 * @return	string				Stripe customer ref 'cu_xxxxxxxxxxxxx' or ''
127
	 */
128
	public function getStripeCustomerAccount($id, $status = 0)
129
	{
130
		include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php';
131
		$societeaccount = new SocieteAccount($this->db);
132
		return $societeaccount->getCustomerAccount($id, 'stripe', $status);		// Get thirdparty cus_...
133
	}
134
135
136
	/**
137
	 * Get the Stripe customer of a thirdparty (with option to create it if not linked yet)
138
	 *
139
	 * @param	Societe	$object							Object thirdparty to check, or create on stripe (create on stripe also update the stripe_account table for current entity)
140
	 * @param	string	$key							''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe connect
141
	 * @param	int		$status							Status (0=test, 1=live)
142
	 * @param	int		$createifnotlinkedtostripe		1=Create the stripe customer and the link if the thirdparty is not yet linked to a stripe customer
143
	 * @return 	\Stripe\StripeCustomer|null 			Stripe Customer or null if not found
0 ignored issues
show
Bug introduced by
The type Stripe\StripeCustomer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
144
	 */
145
	public function customerStripe(Societe $object, $key = '', $status = 0, $createifnotlinkedtostripe = 0)
146
	{
147
		global $conf, $user;
148
149
		if (empty($object->id))
150
		{
151
			dol_syslog("customerStripe is called with the parameter object that is not loaded");
152
			return null;
153
		}
154
155
		$customer = null;
156
157
		$sql = "SELECT sa.key_account as key_account, sa.entity";			// key_account is cus_....
158
		$sql.= " FROM " . MAIN_DB_PREFIX . "societe_account as sa";
159
		$sql.= " WHERE sa.fk_soc = " . $object->id;
160
		$sql.= " AND sa.entity IN (".getEntity('societe').")";
161
		$sql.= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
162
		$sql.= " AND key_account IS NOT NULL AND key_account <> ''";
163
164
		dol_syslog(get_class($this) . "::customerStripe search stripe customer id for thirdparty id=".$object->id, LOG_DEBUG);
165
		$resql = $this->db->query($sql);
166
		if ($resql) {
167
			$num = $this->db->num_rows($resql);
168
			if ($num)
169
			{
170
				$obj = $this->db->fetch_object($resql);
171
				$tiers = $obj->key_account;
172
173
				dol_syslog(get_class($this) . "::customerStripe found stripe customer key_account = ".$tiers);
174
175
				// Force to use the correct API key
176
				global $stripearrayofkeysbyenv;
177
				\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
178
179
				try {
180
					if (empty($key)) {				// If the Stripe connect account not set, we use common API usage
181
						$customer = \Stripe\Customer::retrieve("$tiers");
182
					} else {
183
						$customer = \Stripe\Customer::retrieve("$tiers", array("stripe_account" => $key));
184
					}
185
				}
186
				catch(Exception $e)
187
				{
188
					// 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.'
189
					$this->error = $e->getMessage();
190
				}
191
			}
192
			elseif ($createifnotlinkedtostripe)
193
			{
194
			    $ipaddress = getUserRemoteIP();
195
196
				$dataforcustomer = array(
197
					"email" => $object->email,
198
					"description" => $object->name,
199
					"metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress)
200
				);
201
202
				$vatcleaned = $object->tva_intra ? $object->tva_intra : null;
203
204
				/*
205
				$taxinfo = array('type'=>'vat');
206
				if ($vatcleaned)
207
				{
208
					$taxinfo["tax_id"] = $vatcleaned;
209
				}
210
				// We force data to "null" if not defined as expected by Stripe
211
				if (empty($vatcleaned)) $taxinfo=null;
212
				$dataforcustomer["tax_info"] = $taxinfo;
213
				*/
214
215
				//$a = \Stripe\Stripe::getApiKey();
216
				//var_dump($a);var_dump($key);exit;
217
				try {
218
					// Force to use the correct API key
219
					global $stripearrayofkeysbyenv;
220
					\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
221
222
					if (empty($key)) {				// If the Stripe connect account not set, we use common API usage
223
						$customer = \Stripe\Customer::create($dataforcustomer);
224
					} else {
225
						$customer = \Stripe\Customer::create($dataforcustomer, array("stripe_account" => $key));
226
					}
227
228
					// Create the VAT record in Stripe
229
					if (! empty($conf->global->STRIPE_SAVE_TAX_IDS))	// We setup to save Tax info on Stripe side. Warning: This may result in error when saving customer
230
					{
231
						if (! empty($vatcleaned))
232
						{
233
							$isineec=isInEEC($object);
234
							if ($object->country_code && $isineec)
235
							{
236
								//$taxids = $customer->allTaxIds($customer->id);
237
								$customer->createTaxId($customer->id, array('type'=>'eu_vat', 'value'=>$vatcleaned));
238
							}
239
						}
240
					}
241
242
					// Create customer in Dolibarr
243
					$sql = "INSERT INTO " . MAIN_DB_PREFIX . "societe_account (fk_soc, login, key_account, site, status, entity, date_creation, fk_user_creat)";
244
					$sql .= " VALUES (".$object->id.", '', '".$this->db->escape($customer->id)."', 'stripe', " . $status . ", " . $conf->entity . ", '".$this->db->idate(dol_now())."', ".$user->id.")";
245
					$resql = $this->db->query($sql);
246
					if (! $resql)
247
					{
248
						$this->error = $this->db->lasterror();
249
					}
250
				}
251
				catch(Exception $e)
252
				{
253
					$this->error = $e->getMessage();
254
				}
255
			}
256
		}
257
		else
258
		{
259
			dol_print_error($this->db);
260
		}
261
262
		return $customer;
263
	}
264
265
	/**
266
	 * Get the Stripe payment method Object from its ID
267
	 *
268
	 * @param	string	$paymentmethod	   			Payment Method ID
269
	 * @param	string	$key						''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe connect
270
	 * @param	int		$status						Status (0=test, 1=live)
271
	 * @return 	\Stripe\PaymentMethod|null 			Stripe PaymentMethod or null if not found
272
	 */
273
	public function getPaymentMethodStripe($paymentmethod, $key = '', $status = 0)
274
	{
275
		$stripepaymentmethod = null;
276
277
		try {
278
			// Force to use the correct API key
279
			global $stripearrayofkeysbyenv;
280
			\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
281
			if (empty($key)) {				// If the Stripe connect account not set, we use common API usage
282
				$stripepaymentmethod = \Stripe\PaymentMethod::retrieve(''.$paymentmethod->id.'');
0 ignored issues
show
Bug introduced by
The property id does not exist on string.
Loading history...
283
			} else {
284
				$stripepaymentmethod = \Stripe\PaymentMethod::retrieve(''.$paymentmethod->id.'', array("stripe_account" => $key));
285
			}
286
		}
287
		catch(Exception $e)
288
		{
289
			$this->error = $e->getMessage();
290
		}
291
292
		return $stripepaymentmethod;
293
	}
294
295
    /**
296
	 * Get the Stripe payment intent. Create it with confirmnow=false
297
     * Warning. If a payment was tried and failed, a payment intent was created.
298
	 * But if we change something on object to pay (amount or other), reusing same payment intent is not allowed.
299
	 * Recommanded solution is to recreate a new payment intent each time we need one (old one will be automatically closed after a delay),
300
	 * that's why i comment the part of code to retreive a payment intent with object id (never mind if we cumulate payment intent with old ones that will not be used)
301
	 * Note: This is used when option STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION is on when making a payment from the public/payment/newpayment.php page
302
	 * but not when using the STRIPE_USE_NEW_CHECKOUT.
303
	 *
304
	 * @param   double  $amount                             Amount
305
	 * @param   string  $currency_code                      Currency code
306
	 * @param   string  $tag                                Tag
307
	 * @param   string  $description                        Description
308
	 * @param	Societe	$object							    Object to pay with Stripe
309
	 * @param	string 	$customer							Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe()
310
	 * @param	string	$key							    ''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe connect
311
	 * @param	int		$status							    Status (0=test, 1=live)
312
	 * @param	int		$usethirdpartyemailforreceiptemail	1=use thirdparty email for receipt
313
	 * @param	int		$mode		                        automatic=automatic confirmation/payment when conditions are ok, manual=need to call confirm() on intent
314
	 * @param   boolean $confirmnow                         false=default, true=try to confirm immediatly after create (if conditions are ok)
315
	 * @return 	\Stripe\PaymentIntent|null 			        Stripe PaymentIntent or null if not found and failed to create
316
	 */
317
	public function getPaymentIntent($amount, $currency_code, $tag, $description = '', $object = null, $customer = null, $key = null, $status = 0, $usethirdpartyemailforreceiptemail = 0, $mode = 'automatic', $confirmnow = false)
318
	{
319
		global $conf;
320
321
		dol_syslog("getPaymentIntent", LOG_INFO, 1);
322
323
		$error = 0;
324
325
		if (empty($status)) $service = 'StripeTest';
326
		else $service = 'StripeLive';
327
328
		$arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
329
		if (! in_array($currency_code, $arrayzerounitcurrency)) $stripeamount = $amount * 100;
330
		else $stripeamount = $amount;
331
332
		$fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
333
		if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
334
		    $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
335
		} elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
336
		    $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
337
		}
338
				if (! in_array($currency, $arrayzerounitcurrency)) $stripefee = round($fee * 100);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $currency does not exist. Did you maybe mean $arrayzerounitcurrency?
Loading history...
339
				else $stripefee = round($fee);
340
341
		$paymentintent = null;
342
343
		if (is_object($object))
344
		{
345
			// Warning. If a payment was tried and failed, a payment intent was created.
346
			// But if we change someting on object to pay (amount or other), reusing same payment intent is not allowed.
347
			// Recommanded solution is to recreate a new payment intent each time we need one (old one will be automatically closed after a delay),
348
			// that's why i comment the part of code to retreive a payment intent with object id (never mind if we cumulate payment intent with old that will not be used)
349
			/*
350
			$sql = "SELECT pi.ext_payment_id, pi.entity, pi.fk_facture, pi.sourcetype, pi.ext_payment_site";
351
    		$sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_facture_demande as pi";
352
    		$sql.= " WHERE pi.fk_facture = " . $object->id;
353
    		$sql.= " AND pi.sourcetype = '" . $object->element . "'";
354
    		$sql.= " AND pi.entity IN (".getEntity('societe').")";
355
    		$sql.= " AND pi.ext_payment_site = '" . $service . "'";
356
357
    		dol_syslog(get_class($this) . "::getPaymentIntent search stripe payment intent for object id = ".$object->id, LOG_DEBUG);
358
    		$resql = $this->db->query($sql);
359
    		if ($resql) {
360
    			$num = $this->db->num_rows($resql);
361
    			if ($num)
362
    			{
363
    				$obj = $this->db->fetch_object($resql);
364
    				$intent = $obj->ext_payment_id;
365
366
    				dol_syslog(get_class($this) . "::getPaymentIntent found existing payment intent record");
367
368
    				// Force to use the correct API key
369
    				global $stripearrayofkeysbyenv;
370
    				\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
371
372
    				try {
373
    					if (empty($key)) {				// If the Stripe connect account not set, we use common API usage
374
    						$paymentintent = \Stripe\PaymentIntent::retrieve($intent);
375
    					} else {
376
    						$paymentintent = \Stripe\PaymentIntent::retrieve($intent, array("stripe_account" => $key));
377
    					}
378
    				}
379
    				catch(Exception $e)
380
    				{
381
    				    $error++;
382
    					$this->error = $e->getMessage();
383
    				}
384
    			}
385
    		}*/
386
		}
387
388
		if (empty($paymentintent))
0 ignored issues
show
introduced by
The condition empty($paymentintent) is always false.
Loading history...
389
		{
390
    		$ipaddress=getUserRemoteIP();
391
    		$metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
392
            if (is_object($object))
393
            {
394
                $metadata['dol_type'] = $object->element;
395
                $metadata['dol_id'] = $object->id;
396
				if (is_object($object->thirdparty) && $object->thirdparty->id > 0) $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
397
            }
398
399
    		$dataforintent = array(
400
    		    "confirm" => $confirmnow,	// Do not confirm immediatly during creation of intent
401
    		    "confirmation_method" => $mode,
402
    		    "amount" => $stripeamount,
403
    			"currency" => $currency_code,
404
    		    "payment_method_types" => array("card"),
405
    		    "description" => $description,
406
    		    "statement_descriptor" => dol_trunc($tag, 10, 'right', 'UTF-8', 1),     // 22 chars that appears on bank receipt (company + description)
407
				"setup_future_usage" => "on_session",
408
    			"metadata" => $metadata
409
    		);
410
    		if (! is_null($customer)) $dataforintent["customer"]=$customer;
411
    		// payment_method =
412
    		// payment_method_types = array('card')
413
            //var_dump($dataforintent);
414
415
    		if ($conf->entity!=$conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0)
416
    		{
417
    			$dataforintent["application_fee_amount"] = $stripefee;
418
    		}
419
    		if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email)
420
    		{
421
    		    $dataforintent["receipt_email"] = $object->thirdparty->email;
422
    		}
423
424
    		try {
425
    			// Force to use the correct API key
426
    			global $stripearrayofkeysbyenv;
427
    			\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
428
429
    			// 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.
430
    			if (empty($key)) {				// If the Stripe connect account not set, we use common API usage
431
    				$paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("idempotency_key" => "$description"));
432
    			    //$paymentintent = \Stripe\PaymentIntent::create($dataforintent, array());
433
    			} else {
434
    				$paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
435
    			    //$paymentintent = \Stripe\PaymentIntent::create($dataforintent, array("stripe_account" => $key));
436
    			}
437
    			//var_dump($paymentintent->id);
438
439
    			// Store the payment intent
440
    			if (is_object($object))
441
    			{
442
    				$paymentintentalreadyexists = 0;
443
    				// Check that payment intent $paymentintent->id is not already recorded.
444
    				$sql = "SELECT pi.rowid";
445
    				$sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_facture_demande as pi";
446
    				$sql.= " WHERE pi.entity IN (".getEntity('societe').")";
447
    				$sql.= " AND pi.ext_payment_site = '" . $service . "'";
448
    				$sql.= " AND pi.ext_payment_id = '".$this->db->escape($paymentintent->id)."'";
449
450
    				dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_facture_demande", LOG_DEBUG);
451
    				$resql = $this->db->query($sql);
452
    				if ($resql) {
453
    					$num = $this->db->num_rows($resql);
454
    					if ($num)
455
    					{
456
    						$obj = $this->db->fetch_object($resql);
457
    						if ($obj) $paymentintentalreadyexists++;
458
    					}
459
    				}
460
    				else dol_print_error($this->db);
461
462
    				// If not, we create it.
463
    				if (! $paymentintentalreadyexists)
464
    				{
465
	    				$now=dol_now();
466
	    				$sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_facture_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
467
	    				$sql .= " VALUES ('".$this->db->idate($now)."', '0', '".$this->db->escape($paymentintent->id)."', ".$object->id.", '".$this->db->escape($object->element)."', " . $conf->entity . ", '" . $service . "')";
468
	    				$resql = $this->db->query($sql);
469
	    				if (! $resql)
470
	    				{
471
	    				    $error++;
472
	    					$this->error = $this->db->lasterror();
473
	                        dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$paymentintent->id." into database.");
474
	    				}
475
    				}
476
    			}
477
    			else
478
    			{
479
    			    $_SESSION["stripe_payment_intent"] = $paymentintent;
480
    			}
481
    		}
482
    		catch(Exception $e)
483
    		{
484
    		    /*var_dump($dataforintent);
485
    		    var_dump($description);
486
    		    var_dump($key);
487
    		    var_dump($paymentintent);
488
    		    var_dump($e->getMessage());*/
489
                $error++;
490
    			$this->error = $e->getMessage();
491
    		}
492
		}
493
494
		dol_syslog("getPaymentIntent return error=".$error, LOG_INFO, -1);
495
496
		if (! $error)
497
		{
498
			return $paymentintent;
499
		}
500
		else
501
		{
502
			return null;
503
		}
504
	}
505
506
507
	/**
508
	 * Get the Stripe payment intent. Create it with confirmnow=false
509
	 * Warning. If a payment was tried and failed, a payment intent was created.
510
	 * But if we change something on object to pay (amount or other), reusing same payment intent is not allowed.
511
	 * Recommanded solution is to recreate a new payment intent each time we need one (old one will be automatically closed after a delay),
512
	 * that's why i comment the part of code to retreive a payment intent with object id (never mind if we cumulate payment intent with old ones that will not be used)
513
	 * Note: This is used when option STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION is on when making a payment from the public/payment/newpayment.php page
514
	 * but not when using the STRIPE_USE_NEW_CHECKOUT.
515
	 *
516
	 * @param   string  $description                        Description
517
	 * @param	Societe	$object							    Object to pay with Stripe
518
	 * @param	string 	$customer							Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe()
519
	 * @param	string	$key							    ''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe connect
520
	 * @param	int		$status							    Status (0=test, 1=live)
521
	 * @param	int		$usethirdpartyemailforreceiptemail	1=use thirdparty email for receipt
522
	 * @param   boolean $confirmnow                         false=default, true=try to confirm immediatly after create (if conditions are ok)
523
	 * @return 	\Stripe\SetupIntent|null 			        Stripe SetupIntent or null if not found and failed to create
524
	 */
525
	public function getSetupIntent($description, $object, $customer, $key, $status, $usethirdpartyemailforreceiptemail = 0, $confirmnow = false)
526
	{
527
		global $conf;
528
529
		dol_syslog("getSetupIntent", LOG_INFO, 1);
530
531
		$error = 0;
532
533
		if (empty($status)) $service = 'StripeTest';
534
		else $service = 'StripeLive';
535
536
		$setupintent = null;
537
538
		if (empty($setupintent))
0 ignored issues
show
introduced by
The condition empty($setupintent) is always false.
Loading history...
539
		{
540
			$ipaddress=getUserRemoteIP();
541
			$metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
542
			if (is_object($object))
543
			{
544
				$metadata['dol_type'] = $object->element;
545
				$metadata['dol_id'] = $object->id;
546
				if (is_object($object->thirdparty) && $object->thirdparty->id > 0) $metadata['dol_thirdparty_id'] = $object->thirdparty->id;
547
			}
548
549
			$dataforintent = array(
550
				"confirm" => $confirmnow,	// Do not confirm immediatly during creation of intent
551
				"payment_method_types" => array("card"),
552
				"description" => $description,
553
				"usage" => "off_session",
554
				"metadata" => $metadata
555
			);
556
			if (! is_null($customer)) $dataforintent["customer"]=$customer;
557
			// payment_method =
558
			// payment_method_types = array('card')
559
			//var_dump($dataforintent);
560
561
			if ($usethirdpartyemailforreceiptemail && is_object($object) && $object->thirdparty->email)
562
			{
563
				$dataforintent["receipt_email"] = $object->thirdparty->email;
564
			}
565
566
			try {
567
				// Force to use the correct API key
568
				global $stripearrayofkeysbyenv;
569
				\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
570
571
				// 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.
572
				if (empty($key)) {				// If the Stripe connect account not set, we use common API usage
573
					//$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description"));
574
					$setupintent = \Stripe\SetupIntent::create($dataforintent, array());
575
				} else {
576
					//$setupintent = \Stripe\SetupIntent::create($dataforintent, array("idempotency_key" => "$description", "stripe_account" => $key));
577
					$setupintent = \Stripe\SetupIntent::create($dataforintent, array("stripe_account" => $key));
578
				}
579
				//var_dump($setupintent->id);
580
581
				// Store the setup intent
582
				/*if (is_object($object))
583
				{
584
					$setupintentalreadyexists = 0;
585
					// Check that payment intent $setupintent->id is not already recorded.
586
					$sql = "SELECT pi.rowid";
587
					$sql.= " FROM " . MAIN_DB_PREFIX . "prelevement_facture_demande as pi";
588
					$sql.= " WHERE pi.entity IN (".getEntity('societe').")";
589
					$sql.= " AND pi.ext_payment_site = '" . $service . "'";
590
					$sql.= " AND pi.ext_payment_id = '".$this->db->escape($setupintent->id)."'";
591
592
					dol_syslog(get_class($this) . "::getPaymentIntent search if payment intent already in prelevement_facture_demande", LOG_DEBUG);
593
					$resql = $this->db->query($sql);
594
					if ($resql) {
595
						$num = $this->db->num_rows($resql);
596
						if ($num)
597
						{
598
							$obj = $this->db->fetch_object($resql);
599
							if ($obj) $setupintentalreadyexists++;
600
						}
601
					}
602
					else dol_print_error($this->db);
603
604
					// If not, we create it.
605
					if (! $setupintentalreadyexists)
606
					{
607
						$now=dol_now();
608
						$sql = "INSERT INTO " . MAIN_DB_PREFIX . "prelevement_facture_demande (date_demande, fk_user_demande, ext_payment_id, fk_facture, sourcetype, entity, ext_payment_site)";
609
						$sql .= " VALUES ('".$this->db->idate($now)."', '0', '".$this->db->escape($setupintent->id)."', ".$object->id.", '".$this->db->escape($object->element)."', " . $conf->entity . ", '" . $service . "')";
610
						$resql = $this->db->query($sql);
611
						if (! $resql)
612
						{
613
							$error++;
614
							$this->error = $this->db->lasterror();
615
							dol_syslog(get_class($this) . "::PaymentIntent failed to insert paymentintent with id=".$setupintent->id." into database.");
616
						}
617
					}
618
				}
619
				else
620
				{
621
					$_SESSION["stripe_setup_intent"] = $setupintent;
622
				}*/
623
			}
624
			catch(Exception $e)
625
			{
626
				/*var_dump($dataforintent);
627
				 var_dump($description);
628
				 var_dump($key);
629
				 var_dump($setupintent);
630
				 var_dump($e->getMessage());*/
631
				$error++;
632
				$this->error = $e->getMessage();
633
			}
634
		}
635
636
		dol_syslog("getSetupIntent return error=".$error, LOG_INFO, -1);
637
638
		if (! $error)
639
		{
640
			return $setupintent;
641
		}
642
		else
643
		{
644
			return null;
645
		}
646
	}
647
648
649
	/**
650
	 * Get the Stripe card of a company payment mode (with option to create it on Stripe if not linked yet)
651
	 *
652
	 * @param	\Stripe\StripeCustomer	$cu								Object stripe customer
653
	 * @param	CompanyPaymentMode		$object							Object companypaymentmode to check, or create on stripe (create on stripe also update the societe_rib table for current entity)
654
	 * @param	string					$stripeacc						''=Use common API. If not '', it is the Stripe connect account 'acc_....' to use Stripe connect
655
	 * @param	int						$status							Status (0=test, 1=live)
656
	 * @param	int						$createifnotlinkedtostripe		1=Create the stripe card and the link if the card is not yet linked to a stripe card
657
	 * @return 	\Stripe\StripeCard|null 								Stripe Card or null if not found
0 ignored issues
show
Bug introduced by
The type Stripe\StripeCard was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
658
	 */
659
	public function cardStripe($cu, CompanyPaymentMode $object, $stripeacc = '', $status = 0, $createifnotlinkedtostripe = 0)
660
	{
661
		global $conf, $user;
662
663
		$card = null;
664
665
		$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_....
666
		$sql.= " FROM " . MAIN_DB_PREFIX . "societe_rib as sa";
667
		$sql.= " WHERE sa.rowid = " . $object->id;		// We get record from ID, no need for filter on entity
668
		$sql.= " AND sa.type = 'card'";
669
670
		dol_syslog(get_class($this) . "::fetch search stripe card id for paymentmode id=".$object->id.", stripeacc=".$stripeacc.", status=".$status.", createifnotlinkedtostripe=".$createifnotlinkedtostripe, LOG_DEBUG);
671
		$resql = $this->db->query($sql);
672
		if ($resql) {
673
			$num = $this->db->num_rows($resql);
674
			if ($num)
675
			{
676
				$obj = $this->db->fetch_object($resql);
677
				$cardref = $obj->stripe_card_ref;
678
				dol_syslog(get_class($this) . "::cardStripe cardref=".$cardref);
679
				if ($cardref)
680
				{
681
					try {
682
						if (empty($stripeacc)) {				// If the Stripe connect account not set, we use common API usage
683
							$card = $cu->sources->retrieve($cardref);
684
						} else {
685
							//$card = $cu->sources->retrieve($cardref, array("stripe_account" => $stripeacc));		// this API fails when array stripe_account is provided
686
							$card = $cu->sources->retrieve($cardref);
687
						}
688
					}
689
					catch(Exception $e)
690
					{
691
						$this->error = $e->getMessage();
692
						dol_syslog($this->error, LOG_WARNING);
693
					}
694
				}
695
				elseif ($createifnotlinkedtostripe)
696
				{
697
					$exp_date_month=$obj->exp_date_month;
698
					$exp_date_year=$obj->exp_date_year;
699
					$number=$obj->number;
700
					$cvc=$obj->cvn;								// cvn in database, cvc for stripe
701
					$cardholdername=$obj->proprio;
702
703
					$dataforcard = array(
704
						"source" => array('object'=>'card', 'exp_month'=>$exp_date_month, 'exp_year'=>$exp_date_year, 'number'=>$number, 'cvc'=>$cvc, 'name'=>$cardholdername),
705
						"metadata" => array('dol_id'=>$object->id, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR']))
706
					);
707
708
					//$a = \Stripe\Stripe::getApiKey();
709
					//var_dump($a);var_dump($stripeacc);exit;
710
					dol_syslog("Try to create card dataforcard = ".json_encode($dataforcard));
711
					try {
712
						if (empty($stripeacc)) {				// If the Stripe connect account not set, we use common API usage
713
							$card = $cu->sources->create($dataforcard);
714
						} else {
715
							$card = $cu->sources->create($dataforcard, array("stripe_account" => $stripeacc));
716
						}
717
718
						if ($card)
719
						{
720
							$sql = "UPDATE " . MAIN_DB_PREFIX . "societe_rib";
721
							$sql.= " SET stripe_card_ref = '".$this->db->escape($card->id)."', card_type = '".$this->db->escape($card->brand)."',";
722
							$sql.= " country_code = '".$this->db->escape($card->country)."',";
723
							$sql.= " approved = ".($card->cvc_check == 'pass' ? 1 : 0);
724
							$sql.= " WHERE rowid = " . $object->id;
725
							$sql.= " AND type = 'card'";
726
							$resql = $this->db->query($sql);
727
							if (! $resql)
728
							{
729
								$this->error = $this->db->lasterror();
730
							}
731
						}
732
						else
733
						{
734
							$this->error = 'Call to cu->source->create return empty card';
735
						}
736
					}
737
					catch(Exception $e)
738
					{
739
						$this->error = $e->getMessage();
740
						dol_syslog($this->error, LOG_WARNING);
741
					}
742
				}
743
			}
744
		}
745
		else
746
		{
747
			dol_print_error($this->db);
748
		}
749
750
		return $card;
751
	}
752
753
	/**
754
	 * Create charge with public/payment/newpayment.php, stripe/card.php, cronjobs or REST API
755
	 * This is using old Stripe API charge.
756
	 *
757
	 * @param	int 	$amount									Amount to pay
758
	 * @param	string 	$currency								EUR, GPB...
759
	 * @param	string 	$origin									Object type to pay (order, invoice, contract...)
760
	 * @param	int 	$item									Object id to pay
761
	 * @param	string 	$source									src_xxxxx or card_xxxxx
762
	 * @param	string 	$customer								Stripe customer ref 'cus_xxxxxxxxxxxxx' via customerStripe()
763
	 * @param	string 	$account								Stripe account ref 'acc_xxxxxxxxxxxxx' via  getStripeAccount()
764
	 * @param	int		$status									Status (0=test, 1=live)
765
	 * @param	int		$usethirdpartyemailforreceiptemail		Use thirdparty email as receipt email
766
	 * @param	boolean	$capture								Set capture flag to true (take payment) or false (wait)
767
	 * @return Stripe
768
	 */
769
	public function createPaymentStripe($amount, $currency, $origin, $item, $source, $customer, $account, $status = 0, $usethirdpartyemailforreceiptemail = 0, $capture = true)
770
	{
771
		global $conf;
772
773
		$error = 0;
774
775
		if (empty($status)) $service = 'StripeTest';
776
		else $service = 'StripeLive';
777
778
		$sql = "SELECT sa.key_account as key_account, sa.fk_soc, sa.entity";
779
		$sql.= " FROM " . MAIN_DB_PREFIX . "societe_account as sa";
780
		$sql.= " WHERE sa.key_account = '" . $this->db->escape($customer) . "'";
781
		//$sql.= " AND sa.entity IN (".getEntity('societe').")";
782
		$sql.= " AND sa.site = 'stripe' AND sa.status = ".((int) $status);
783
784
		dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
785
		$result = $this->db->query($sql);
786
		if ($result) {
787
			if ($this->db->num_rows($result)) {
788
				$obj = $this->db->fetch_object($result);
789
				$key = $obj->fk_soc;
790
			} else {
791
				$key = null;
792
			}
793
		} else {
794
			$key = null;
795
		}
796
797
		$arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF');
798
		if (! in_array($currency, $arrayzerounitcurrency)) $stripeamount=$amount * 100;
799
		else $stripeamount = $amount;
800
801
		$societe = new Societe($this->db);
802
		if ($key > 0) $societe->fetch($key);
803
804
		$description = "";
805
		$ref = "";
806
		if ($origin == order) {
0 ignored issues
show
Bug introduced by
The constant order was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
807
			$order = new Commande($this->db);
808
			$order->fetch($item);
809
			$ref = $order->ref;
810
			$description = "ORD=" . $ref . ".CUS=" . $societe->id.".PM=stripe";
811
		} elseif ($origin == invoice) {
0 ignored issues
show
Bug introduced by
The constant invoice was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
812
			$invoice = new Facture($this->db);
813
			$invoice->fetch($item);
814
			$ref = $invoice->ref;
815
			$description = "INV=" . $ref . ".CUS=" . $societe->id.".PM=stripe";
816
		}
817
818
		$metadata = array(
819
			"dol_id" => "" . $item . "",
820
			"dol_type" => "" . $origin . "",
821
			"dol_thirdparty_id" => "" . $societe->id . "",
822
			'dol_thirdparty_name' => $societe->name,
823
			'dol_version'=>DOL_VERSION,
824
			'dol_entity'=>$conf->entity,
825
			'ipaddress'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR'])
826
		);
827
		$return = new Stripe($this->db);
828
		try {
829
			// Force to use the correct API key
830
			global $stripearrayofkeysbyenv;
831
			\Stripe\Stripe::setApiKey($stripearrayofkeysbyenv[$status]['secret_key']);
832
833
			if (empty($conf->stripeconnect->enabled))
834
			{
835
				if (preg_match('/acct_/i', $source))
836
				{
837
                    $charge = \Stripe\Charge::create(array(
838
						"amount" => "$stripeamount",
839
						"currency" => "$currency",
840
                        "statement_descriptor" => dol_trunc($description, 10, 'right', 'UTF-8', 1),     // 22 chars that appears on bank receipt (company + description)
841
						"description" => "Stripe payment: ".$description,
842
						"capture"  => $capture,
843
						"metadata" => $metadata,
844
						"source" => "$source"
845
					));
846
				} else {
847
					$paymentarray = array(
848
						"amount" => "$stripeamount",
849
						"currency" => "$currency",
850
					    "statement_descriptor" => dol_trunc($description, 10, 'right', 'UTF-8', 1),     // 22 chars that appears on bank receipt (company + description)
851
						"description" => "Stripe payment: ".$description,
852
						"capture"  => $capture,
853
						"metadata" => $metadata,
854
						"source" => "$source",
855
						"customer" => "$customer"
856
					);
857
858
					if ($societe->email && $usethirdpartyemailforreceiptemail)
859
					{
860
						$paymentarray["receipt_email"] = $societe->email;
861
					}
862
863
					$charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description"));
864
				}
865
			} else {
866
		$fee = $amount * ($conf->global->STRIPE_APPLICATION_FEE_PERCENT / 100) + $conf->global->STRIPE_APPLICATION_FEE;
867
		if ($fee >= $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL && $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL > $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
868
		    $fee = $conf->global->STRIPE_APPLICATION_FEE_MAXIMAL;
869
		} elseif ($fee < $conf->global->STRIPE_APPLICATION_FEE_MINIMAL) {
870
		    $fee = $conf->global->STRIPE_APPLICATION_FEE_MINIMAL;
871
		}
872
				if (! in_array($currency, $arrayzerounitcurrency)) $stripefee = round($fee * 100);
873
				else $stripefee = round($fee);
874
875
        		$paymentarray = array(
876
					"amount" => "$stripeamount",
877
					"currency" => "$currency",
878
        		    "statement_descriptor" => dol_trunc($description, 10, 'right', 'UTF-8', 1),     // 22 chars that appears on bank receipt (company + description)
879
					"description" => "Stripe payment: ".$description,
880
					"capture"  => $capture,
881
					"metadata" => $metadata,
882
					"source" => "$source",
883
					"customer" => "$customer"
884
				);
885
				if ($conf->entity!=$conf->global->STRIPECONNECT_PRINCIPAL && $stripefee > 0)
886
				{
887
					$paymentarray["application_fee_amount"] = $stripefee;
888
				}
889
				if ($societe->email && $usethirdpartyemailforreceiptemail)
890
				{
891
					$paymentarray["receipt_email"] = $societe->email;
892
				}
893
894
				$charge = \Stripe\Charge::create($paymentarray, array("idempotency_key" => "$description", "stripe_account" => "$account"));
895
			}
896
			if (isset($charge->id)) {}
897
898
			$return->statut = 'success';
899
			$return->id = $charge->id;
900
			if ($charge->source->type == 'card') {
901
				$return->message = $charge->source->card->brand . " ...." . $charge->source->card->last4;
902
			} elseif ($charge->source->type == 'three_d_secure') {
903
				$stripe = new Stripe($this->db);
904
				$src = \Stripe\Source::retrieve("" . $charge->source->three_d_secure->card . "", array(
905
				"stripe_account" => $stripe->getStripeAccount($service)
906
				));
907
				$return->message = $src->card->brand . " ...." . $src->card->last4;
908
			} else {
909
				$return->message = $charge->id;
910
			}
911
		} catch (\Stripe\Error\Card $e) {
912
			include DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
913
			// Since it's a decline, \Stripe\Error\Card will be caught
914
			$body = $e->getJsonBody();
915
			$err = $body['error'];
916
917
			$return->statut = 'error';
918
			$return->id = $err['charge'];
919
			$return->type = $err['type'];
920
			$return->code = $err['code'];
921
			$return->message = $err['message'];
922
			$body = "Error: <br>" . $return->id . " " . $return->message . " ";
923
			$subject = '[Alert] Payment error using Stripe';
924
			$cmailfile = new CMailFile($subject, $conf->global->ONLINE_PAYMENT_SENDEMAIL, $conf->global->MAIN_INFO_SOCIETE_MAIL, $body);
925
			$cmailfile->sendfile();
926
927
			$error++;
928
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
929
		} catch (\Stripe\Error\RateLimit $e) {
930
			// Too many requests made to the API too quickly
931
			$error++;
932
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
933
		} catch (\Stripe\Error\InvalidRequest $e) {
934
			// Invalid parameters were supplied to Stripe's API
935
			$error++;
936
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
937
		} catch (\Stripe\Error\Authentication $e) {
938
			// Authentication with Stripe's API failed
939
			// (maybe you changed API keys recently)
940
			$error++;
941
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
942
		} catch (\Stripe\Error\ApiConnection $e) {
943
			// Network communication with Stripe failed
944
			$error++;
945
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
946
		} catch (\Stripe\Error\Base $e) {
947
			// Display a very generic error to the user, and maybe send
948
			// yourself an email
949
			$error++;
950
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
951
		} catch (Exception $e) {
952
			// Something else happened, completely unrelated to Stripe
953
			$error++;
954
			dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe');
955
		}
956
		return $return;
957
	}
958
}
959