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

Stripe::getPaymentIntent()   F

Complexity

Conditions 27
Paths > 20000

Size

Total Lines 186
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 27
eloc 80
nc 200544
nop 11
dl 0
loc 186
rs 0
c 1
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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