Passed
Push — master ( bb4da3...2f00ce )
by Brian
04:35 queued 12s
created

get_customer_profile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 11
rs 10
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * Authorize.net payment gateway
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * Authorize.net Payment Gateway class.
11
 *
12
 */
13
class GetPaid_Authorize_Net_Gateway extends GetPaid_Authorize_Net_Legacy_Gateway {
14
15
    /**
16
	 * Payment method id.
17
	 *
18
	 * @var string
19
	 */
20
    public $id = 'authorizenet';
21
22
    /**
23
	 * An array of features that this gateway supports.
24
	 *
25
	 * @var array
26
	 */
27
    protected $supports = array( 'subscription', 'sandbox', 'tokens', 'addons', 'single_subscription_group', 'multiple_subscription_groups' );
28
29
    /**
30
	 * Payment method order.
31
	 *
32
	 * @var int
33
	 */
34
    public $order = 4;
35
36
    /**
37
	 * Endpoint for requests from Authorize.net.
38
	 *
39
	 * @var string
40
	 */
41
	protected $notify_url;
42
43
	/**
44
	 * Endpoint for requests to Authorize.net.
45
	 *
46
	 * @var string
47
	 */
48
    protected $endpoint;
49
50
    /**
51
	 * Currencies this gateway is allowed for.
52
	 *
53
	 * @var array
54
	 */
55
	public $currencies = array( 'USD', 'CAD', 'GBP', 'DKK', 'NOK', 'PLN', 'SEK', 'AUD', 'EUR', 'NZD' );
56
57
    /**
58
	 * URL to view a transaction.
59
	 *
60
	 * @var string
61
	 */
62
    public $view_transaction_url = 'https://{sandbox}authorize.net/ui/themes/sandbox/Transaction/TransactionReceipt.aspx?transid=%s';
63
64
    /**
65
	 * Class constructor.
66
	 */
67
	public function __construct() {
68
69
        $this->title                = __( 'Credit Card / Debit Card', 'invoicing' );
70
        $this->method_title         = __( 'Authorize.Net', 'invoicing' );
71
        $this->notify_url           = getpaid_get_non_query_string_ipn_url( $this->id );
72
73
        add_filter( 'getpaid_daily_maintenance_should_expire_subscription', array( $this, 'maybe_renew_subscription' ), 10, 2 );
74
        add_filter( 'getpaid_authorizenet_sandbox_notice', array( $this, 'sandbox_notice' ) );
75
        parent::__construct();
76
    }
77
78
    /**
79
	 * Displays the payment method select field.
80
	 *
81
	 * @param int $invoice_id 0 or invoice id.
82
	 * @param GetPaid_Payment_Form $form Current payment form.
83
	 */
84
    public function payment_fields( $invoice_id, $form ) {
85
86
        // Let the user select a payment method.
87
        echo $this->saved_payment_methods();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->saved_payment_methods() targeting GetPaid_Payment_Gateway::saved_payment_methods() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
88
89
        // Show the credit card entry form.
90
        echo $this->new_payment_method_entry( $this->get_cc_form( true ) );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->new_payment_metho...his->get_cc_form(true)) targeting GetPaid_Payment_Gateway:..._payment_method_entry() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
91
    }
92
93
    /**
94
	 * Creates a customer profile.
95
	 *
96
	 *
97
	 * @param WPInv_Invoice $invoice Invoice.
98
     * @param array $submission_data Posted checkout fields.
99
     * @param bool $save Whether or not to save the payment as a token.
100
     * @link https://developer.authorize.net/api/reference/index.html#customer-profiles-create-customer-profile
101
	 * @return string|WP_Error Payment profile id.
102
	 */
103
	public function create_customer_profile( $invoice, $submission_data, $save = true ) {
104
105
        // Remove non-digits from the number
106
        $submission_data['authorizenet']['cc_number'] = preg_replace('/\D/', '', $submission_data['authorizenet']['cc_number'] );
107
108
        // Generate args.
109
        $args = array(
110
            'createCustomerProfileRequest' => array(
111
                'merchantAuthentication'   => $this->get_auth_params(),
112
                'profile'                  => array(
113
                    'merchantCustomerId'   => getpaid_limit_length( $invoice->get_user_id(), 20 ),
114
                    'description'          => getpaid_limit_length( $invoice->get_full_name(), 255 ),
115
                    'email'                => getpaid_limit_length( $invoice->get_email(), 255 ),
116
                    'paymentProfiles'      => array(
117
                        'customerType'     => 'individual',
118
119
                        // Billing information.
120
                        'billTo'           => array(
121
                            'firstName'    => getpaid_limit_length( $invoice->get_first_name(), 50 ),
122
                            'lastName'     => getpaid_limit_length( $invoice->get_last_name(), 50 ),
123
                            'address'      => getpaid_limit_length( $invoice->get_address(), 60 ),
124
                            'city'         => getpaid_limit_length( $invoice->get_city(), 40 ),
125
                            'state'        => getpaid_limit_length( $invoice->get_state(), 40 ),
126
                            'zip'          => getpaid_limit_length( $invoice->get_zip(), 20 ),
127
                            'country'      => getpaid_limit_length( $invoice->get_country(), 60 ),
128
                        ),
129
130
                        // Payment information.
131
                        'payment'          => $this->get_payment_information( $submission_data['authorizenet'] ),
132
                    )
133
                ),
134
                'validationMode'           => $this->is_sandbox( $invoice ) ? 'testMode' : 'liveMode',
135
            )
136
        );
137
138
        $response = $this->post( apply_filters( 'getpaid_authorizenet_customer_profile_args', $args, $invoice ), $invoice );
139
140
        if ( is_wp_error( $response ) ) {
141
142
            // In case the customer profile already exists remotely.
143
            if ( 'E00039' == $response->get_error_code() ) {
0 ignored issues
show
Bug introduced by
The method get_error_code() does not exist on stdClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
            if ( 'E00039' == $response->/** @scrutinizer ignore-call */ get_error_code() ) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
144
                $customer_profile_id = str_replace( 'A duplicate record with ID ', '', $response->get_error_message() );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not exist on stdClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

144
                $customer_profile_id = str_replace( 'A duplicate record with ID ', '', $response->/** @scrutinizer ignore-call */ get_error_message() );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
145
                $customer_profile_id = str_replace( ' already exists.', '', $customer_profile_id );
146
                return $this->create_customer_payment_profile( trim( $customer_profile_id ), $invoice, $submission_data, $save );
147
            }
148
149
            return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response also could return the type stdClass which is incompatible with the documented return type WP_Error|string.
Loading history...
150
        }
151
152
        update_user_meta( $invoice->get_user_id(), $this->get_customer_profile_meta_name( $invoice ), $response->customerProfileId );
0 ignored issues
show
Bug introduced by
The property customerProfileId does not seem to exist on WP_Error.
Loading history...
153
154
        // Save the payment token.
155
        if ( $save ) {
156
            $this->save_token(
157
                array(
158
                    'id'      => $response->customerPaymentProfileIdList[0],
0 ignored issues
show
Bug introduced by
The property customerPaymentProfileIdList does not seem to exist on WP_Error.
Loading history...
159
                    'name'    => getpaid_get_card_name( $submission_data['authorizenet']['cc_number'] ) . '&middot;&middot;&middot;&middot;' . substr( $submission_data['authorizenet']['cc_number'], -4 ),
160
                    'default' => true,
161
                    'type'    => $this->is_sandbox( $invoice ) ? 'sandbox' : 'live',
162
                )
163
            );
164
        }
165
166
        // Add a note about the validation response.
167
        $invoice->add_note(
168
            sprintf( __( 'Created Authorize.NET customer profile: %s', 'invoicing' ), $response->validationDirectResponseList[0] ),
0 ignored issues
show
Bug introduced by
The property validationDirectResponseList does not seem to exist on WP_Error.
Loading history...
169
            false,
170
            false,
171
            true
172
        );
173
174
        return $response->customerPaymentProfileIdList[0];
175
    }
176
177
    /**
178
	 * Retrieves a customer profile.
179
	 *
180
	 *
181
	 * @param string $profile_id profile id.
182
	 * @return string|WP_Error Profile id.
183
     * @link https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-profile
184
	 */
185
	public function get_customer_profile( $profile_id ) {
186
187
        // Generate args.
188
        $args = array(
189
            'getCustomerProfileRequest'  => array(
190
                'merchantAuthentication' => $this->get_auth_params(),
191
                'customerProfileId'      => $profile_id,
192
            )
193
        );
194
195
        return $this->post( $args, false );
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type WPInv_Invoice expected by parameter $invoice of GetPaid_Authorize_Net_Legacy_Gateway::post(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

195
        return $this->post( $args, /** @scrutinizer ignore-type */ false );
Loading history...
Bug Best Practice introduced by
The expression return $this->post($args, false) also could return the type stdClass which is incompatible with the documented return type WP_Error|string.
Loading history...
196
197
    }
198
199
    /**
200
	 * Creates a customer profile.
201
	 *
202
	 *
203
     * @param string $profile_id profile id.
204
	 * @param WPInv_Invoice $invoice Invoice.
205
     * @param array $submission_data Posted checkout fields.
206
     * @param bool $save Whether or not to save the payment as a token.
207
     * @link https://developer.authorize.net/api/reference/index.html#customer-profiles-create-customer-profile
208
	 * @return string|WP_Error Profile id.
209
	 */
210
	public function create_customer_payment_profile( $customer_profile, $invoice, $submission_data, $save ) {
211
212
        // Remove non-digits from the number
213
        $submission_data['authorizenet']['cc_number'] = preg_replace('/\D/', '', $submission_data['authorizenet']['cc_number'] );
214
215
        // Prepare card details.
216
        $payment_information                          = $this->get_payment_information( $submission_data['authorizenet'] );
217
218
        // Authorize.NET does not support saving the same card twice.
219
        $cached_information                           = $this->retrieve_payment_profile_from_cache( $payment_information, $customer_profile, $invoice );
220
221
        if ( $cached_information ) {
222
            return $cached_information;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cached_information returns the type array which is incompatible with the documented return type WP_Error|string.
Loading history...
223
        }
224
225
        // Generate args.
226
        $args = array(
227
            'createCustomerPaymentProfileRequest' => array(
228
                'merchantAuthentication'   => $this->get_auth_params(),
229
                'customerProfileId'        => $customer_profile,
230
                'paymentProfile'           => array(
231
232
                    // Billing information.
233
                    'billTo'           => array(
234
                        'firstName'    => getpaid_limit_length( $invoice->get_first_name(), 50 ),
235
                        'lastName'     => getpaid_limit_length( $invoice->get_last_name(), 50 ),
236
                        'address'      => getpaid_limit_length( $invoice->get_address(), 60 ),
237
                        'city'         => getpaid_limit_length( $invoice->get_city(), 40 ),
238
                        'state'        => getpaid_limit_length( $invoice->get_state(), 40 ),
239
                        'zip'          => getpaid_limit_length( $invoice->get_zip(), 20 ),
240
                        'country'      => getpaid_limit_length( $invoice->get_country(), 60 ),
241
                    ),
242
243
                    // Payment information.
244
                    'payment'          => $payment_information
245
                ),
246
                'validationMode'       => $this->is_sandbox( $invoice ) ? 'testMode' : 'liveMode',
247
            )
248
        );
249
250
        $response = $this->post( apply_filters( 'getpaid_authorizenet_create_customer_payment_profile_args', $args, $invoice ), $invoice );
251
252
        if ( is_wp_error( $response ) ) {
253
            return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response also could return the type stdClass which is incompatible with the documented return type WP_Error|string.
Loading history...
254
        }
255
256
        // Save the payment token.
257
        if ( $save ) {
258
            $this->save_token(
259
                array(
260
                    'id'      => $response->customerPaymentProfileId,
0 ignored issues
show
Bug introduced by
The property customerPaymentProfileId does not seem to exist on WP_Error.
Loading history...
261
                    'name'    => getpaid_get_card_name( $submission_data['authorizenet']['cc_number'] ) . ' &middot;&middot;&middot;&middot; ' . substr( $submission_data['authorizenet']['cc_number'], -4 ),
262
                    'default' => true,
263
                    'type'    => $this->is_sandbox( $invoice ) ? 'sandbox' : 'live',
264
                )
265
            );
266
        }
267
268
        // Cache payment profile id.
269
        $this->add_payment_profile_to_cache( $payment_information, $response->customerPaymentProfileId );
270
271
        // Add a note about the validation response.
272
        $invoice->add_note(
273
            sprintf( __( 'Saved Authorize.NET payment profile: %s', 'invoicing' ), $response->validationDirectResponse ),
0 ignored issues
show
Bug introduced by
The property validationDirectResponse does not seem to exist on WP_Error.
Loading history...
274
            false,
275
            false,
276
            true
277
        );
278
279
280
        return $response->customerPaymentProfileId;
281
    }
282
283
    /**
284
	 * Retrieves payment details from cache.
285
	 *
286
	 *
287
     * @param array $payment_details.
288
	 * @return array|false Profile id.
289
	 */
290
	public function retrieve_payment_profile_from_cache( $payment_details, $customer_profile, $invoice ) {
291
292
        $cached_information = get_option( 'getpaid_authorize_net_cached_profiles', array() );
293
        $payment_details    = hash_hmac( 'sha256', json_encode( $payment_details ), SECURE_AUTH_KEY );
294
295
        if ( ! is_array( $cached_information ) || ! array_key_exists( $payment_details, $cached_information ) ) {
296
            return false;
297
        }
298
299
        // Generate args.
300
        $args = array(
301
            'getCustomerPaymentProfileRequest' => array(
302
                'merchantAuthentication'   => $this->get_auth_params(),
303
                'customerProfileId'        => $customer_profile,
304
                'customerPaymentProfileId' => $cached_information[ $payment_details ],
305
            )
306
        );
307
308
        $response = $this->post( $args, $invoice );
309
310
        return is_wp_error( $response ) ? false : $cached_information[ $payment_details ];
311
312
    }
313
314
    /**
315
	 * Securely adds payment details to cache.
316
	 *
317
	 *
318
     * @param array $payment_details.
319
     * @param string $payment_profile_id.
320
	 */
321
	public function add_payment_profile_to_cache( $payment_details, $payment_profile_id ) {
322
323
        $cached_information = get_option( 'getpaid_authorize_net_cached_profiles', array() );
324
        $cached_information = is_array( $cached_information ) ? $cached_information : array();
325
        $payment_details    = hash_hmac( 'sha256', json_encode( $payment_details ), SECURE_AUTH_KEY );
326
327
        $cached_information[ $payment_details ] = $payment_profile_id;
328
        update_option( 'getpaid_authorize_net_cached_profiles', $cached_information );
329
330
    }
331
332
    /**
333
	 * Retrieves a customer payment profile.
334
	 *
335
	 *
336
	 * @param string $customer_profile_id customer profile id.
337
     * @param string $payment_profile_id payment profile id.
338
	 * @return string|WP_Error Profile id.
339
     * @link https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-payment-profile
340
	 */
341
	public function get_customer_payment_profile( $customer_profile_id, $payment_profile_id ) {
342
343
        // Generate args.
344
        $args = array(
345
            'getCustomerPaymentProfileRequest' => array(
346
                'merchantAuthentication'       => $this->get_auth_params(),
347
                'customerProfileId'            => $customer_profile_id,
348
                'customerPaymentProfileId'     => $payment_profile_id,
349
            )
350
        );
351
352
        return $this->post( $args, false );
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post($args, false) also could return the type stdClass which is incompatible with the documented return type WP_Error|string.
Loading history...
Bug introduced by
false of type false is incompatible with the type WPInv_Invoice expected by parameter $invoice of GetPaid_Authorize_Net_Legacy_Gateway::post(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

352
        return $this->post( $args, /** @scrutinizer ignore-type */ false );
Loading history...
353
354
    }
355
356
    /**
357
	 * Charges a customer payment profile.
358
	 *
359
     * @param string $customer_profile_id customer profile id.
360
     * @param string $payment_profile_id payment profile id.
361
	 * @param WPInv_Invoice $invoice Invoice.
362
     * @link https://developer.authorize.net/api/reference/index.html#payment-transactions-charge-a-customer-profile
363
	 * @return WP_Error|object
364
	 */
365
	public function charge_customer_payment_profile( $customer_profile_id, $payment_profile_id, $invoice ) {
366
367
        // Generate args.
368
        $args = array(
369
370
            'createTransactionRequest'         => array(
371
372
                'merchantAuthentication'       => $this->get_auth_params(),
373
                'refId'                        => $invoice->get_id(),
374
                'transactionRequest'           => array(
375
                    'transactionType'          => 'authCaptureTransaction',
376
                    'amount'                   => $invoice->get_total(),
377
                    'currencyCode'             => $invoice->get_currency(),
378
                    'profile'                  => array(
379
                        'customerProfileId'    => $customer_profile_id,
380
                        'paymentProfile'       => array(
381
                            'paymentProfileId' => $payment_profile_id,
382
                        )
383
                    ),
384
                    'order'                    => array(
385
                        'invoiceNumber'        => getpaid_limit_length( $invoice->get_number(), 20 ),
386
                    ),
387
                    'lineItems'                => array( 'lineItem' => $this->get_line_items( $invoice ) ),
388
                    'tax'                      => array(
389
                        'amount'               => $invoice->get_total_tax(),
390
                        'name'                 => __( 'TAX', 'invoicing' ),
391
                    ),
392
                    'poNumber'                 => getpaid_limit_length( $invoice->get_number(), 25 ),
393
                    'customer'                 => array(
394
                        'id'                   => getpaid_limit_length( $invoice->get_user_id(), 25 ),
395
                        'email'                => getpaid_limit_length( $invoice->get_email(), 25 ),
396
                    ),
397
                    'customerIP'               => $invoice->get_ip(),
398
                )
399
            )
400
        );
401
402
        if ( 0 == $invoice->get_total_tax() ) {
403
            unset( $args['createTransactionRequest']['transactionRequest']['tax'] );
404
        }
405
406
        return $this->post( apply_filters( 'getpaid_authorizenet_charge_customer_payment_profile_args', $args, $invoice ), $invoice );
0 ignored issues
show
Unused Code introduced by
The call to filter_addons_request() has too many arguments starting with $invoice. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

406
        return $this->post( /** @scrutinizer ignore-call */ apply_filters( 'getpaid_authorizenet_charge_customer_payment_profile_args', $args, $invoice ), $invoice );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
407
408
    }
409
410
    /**
411
	 * Processes a customer charge.
412
	 *
413
     * @param stdClass $result Api response.
414
	 * @param WPInv_Invoice $invoice Invoice.
415
	 */
416
	public function process_charge_response( $result, $invoice ) {
417
418
        wpinv_clear_errors();
419
		$response_code = (int) $result->transactionResponse->responseCode;
420
421
		// Succeeded.
422
		if ( 1 == $response_code || 4 == $response_code ) {
423
424
			// Maybe set a transaction id.
425
			if ( ! empty( $result->transactionResponse->transId ) ) {
426
				$invoice->set_transaction_id( $result->transactionResponse->transId );
427
			}
428
429
			$invoice->add_note( sprintf( __( 'Authentication code: %s (%s).', 'invoicing' ), $result->transactionResponse->authCode, $result->transactionResponse->accountNumber ), false, false, true );
430
431
			if ( 1 == $response_code ) {
432
				return $invoice->mark_paid();
433
			}
434
435
			$invoice->set_status( 'wpi-onhold' );
436
        	$invoice->add_note(
437
                sprintf(
438
                    __( 'Held for review: %s', 'invoicing' ),
439
                    $result->transactionResponse->messages->message[0]->description
440
                )
441
			);
442
443
			return $invoice->save();
444
445
		}
446
447
        wpinv_set_error( 'card_declined', __( 'Credit card declined.', 'invoicing' ) );
448
449
        if ( ! empty( $result->transactionResponse->errors ) ) {
450
            $errors = (object) $result->transactionResponse->errors;
451
            wpinv_set_error( $errors->error[0]->errorCode, esc_html( $errors->error[0]->errorText ) );
452
        }
453
454
    }
455
456
    /**
457
	 * Returns payment information.
458
	 *
459
	 *
460
	 * @param array $card Card details.
461
	 * @return array
462
	 */
463
	public function get_payment_information( $card ) {
464
        return array(
465
466
            'creditCard'         => array (
467
                'cardNumber'     => $card['cc_number'],
468
                'expirationDate' => $card['cc_expire_year'] . '-' . $card['cc_expire_month'],
469
                'cardCode'       => $card['cc_cvv2'],
470
            )
471
472
        );
473
    }
474
475
    /**
476
	 * Returns the customer profile meta name.
477
	 *
478
	 *
479
	 * @param WPInv_Invoice $invoice Invoice.
480
	 * @return string
481
	 */
482
	public function get_customer_profile_meta_name( $invoice ) {
483
        return $this->is_sandbox( $invoice ) ? 'getpaid_authorizenet_sandbox_customer_profile_id' : 'getpaid_authorizenet_customer_profile_id';
484
    }
485
486
    /**
487
	 * Validates the submitted data.
488
	 *
489
	 *
490
	 * @param array $submission_data Posted checkout fields.
491
     * @param WPInv_Invoice $invoice
492
	 * @return WP_Error|string The payment profile id
493
	 */
494
	public function validate_submission_data( $submission_data, $invoice ) {
495
496
        // Validate authentication details.
497
        $auth = $this->get_auth_params();
498
499
        if ( empty( $auth['name'] ) || empty( $auth['transactionKey'] ) ) {
500
            return new WP_Error( 'invalid_settings', __( 'Please set-up your login id and transaction key before using this gateway.', 'invoicing') );
501
        }
502
503
        // Validate the payment method.
504
        if ( empty( $submission_data['getpaid-authorizenet-payment-method'] ) ) {
505
            return new WP_Error( 'invalid_payment_method', __( 'Please select a different payment method or add a new card.', 'invoicing') );
506
        }
507
508
        // Are we adding a new payment method?
509
        if ( 'new' != $submission_data['getpaid-authorizenet-payment-method'] ) {
510
            return $submission_data['getpaid-authorizenet-payment-method'];
511
        }
512
513
        // Retrieve the customer profile id.
514
        $profile_id = get_user_meta( $invoice->get_user_id(), $this->get_customer_profile_meta_name( $invoice ), true );
515
516
        // Create payment method.
517
        if ( empty( $profile_id ) ) {
518
            return $this->create_customer_profile( $invoice, $submission_data, ! empty( $submission_data['getpaid-authorizenet-new-payment-method'] ) );
519
        }
520
521
        return $this->create_customer_payment_profile( $profile_id, $invoice, $submission_data, ! empty( $submission_data['getpaid-authorizenet-new-payment-method'] ) );
522
523
    }
524
525
    /**
526
	 * Returns invoice line items.
527
	 *
528
	 *
529
	 * @param WPInv_Invoice $invoice Invoice.
530
	 * @return array
531
	 */
532
	public function get_line_items( $invoice ) {
533
        $items = array();
534
535
        foreach ( $invoice->get_items() as $item ) {
536
537
            $amount  = $invoice->is_renewal() ? $item->get_price() : $item->get_initial_price();
538
            $items[] = array(
539
                'itemId'      => getpaid_limit_length( $item->get_id(), 31 ),
540
                'name'        => getpaid_limit_length( $item->get_raw_name(), 31 ),
541
                'description' => getpaid_limit_length( $item->get_description(), 255 ),
542
                'quantity'    => (string) ( $invoice->get_template() == 'amount' ? 1 : $item->get_quantity() ),
543
                'unitPrice'   => (float) $amount,
544
                'taxable'     => wpinv_use_taxes() && $invoice->is_taxable() && 'tax-exempt' != $item->get_vat_rule(),
545
            );
546
547
        }
548
549
        foreach ( $invoice->get_fees() as $fee_name => $fee ) {
550
551
            $amount  = $invoice->is_renewal() ? $fee['recurring_fee'] : $fee['initial_fee'];
552
553
            if ( $amount > 0 ) {
554
                $items[] = array(
555
                    'itemId'      => getpaid_limit_length( $fee_name, 31 ),
556
                    'name'        => getpaid_limit_length( $fee_name, 31 ),
557
                    'description' => getpaid_limit_length( $fee_name, 255 ),
558
                    'quantity'    => '1',
559
                    'unitPrice'   => (float) $amount,
560
                    'taxable'     => false,
561
                );
562
            }
563
564
        }
565
566
        return $items;
567
    }
568
569
    /**
570
	 * Process Payment.
571
	 *
572
	 *
573
	 * @param WPInv_Invoice $invoice Invoice.
574
	 * @param array $submission_data Posted checkout fields.
575
	 * @param GetPaid_Payment_Form_Submission $submission Checkout submission.
576
	 * @return array
577
	 */
578
	public function process_payment( $invoice, $submission_data, $submission ) {
579
580
        // Validate the submitted data.
581
        $payment_profile_id = $this->validate_submission_data( $submission_data, $invoice );
582
583
        // Do we have an error?
584
        if ( is_wp_error( $payment_profile_id ) ) {
585
            wpinv_set_error( $payment_profile_id->get_error_code(), $payment_profile_id->get_error_message() );
586
            wpinv_send_back_to_checkout( $invoice );
587
        }
588
589
        // Save the payment method to the order.
590
        update_post_meta( $invoice->get_id(), 'getpaid_authorizenet_profile_id', $payment_profile_id );
591
592
        // Check if this is a subscription or not.
593
        $subscriptions = getpaid_get_invoice_subscriptions( $invoice );
594
        if ( ! empty( $subscriptions ) ) {
595
            $this->process_subscription( $invoice, $subscriptions );
596
        }
597
598
        // If it is free, send to the success page.
599
        if ( ! $invoice->needs_payment() ) {
600
            $invoice->mark_paid();
601
            wpinv_send_to_success_page( array( 'invoice_key' => $invoice->get_key() ) );
602
        }
603
604
        // Charge the payment profile.
605
        $this->process_initial_payment( $invoice );
606
607
        wpinv_send_to_success_page( array( 'invoice_key' => $invoice->get_key() ) );
608
609
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
610
611
	}
612
	
613
	/**
614
	 * Processes the initial payment.
615
	 *
616
     * @param WPInv_Invoice $invoice Invoice.
617
	 */
618
	protected function process_initial_payment( $invoice ) {
619
620
		$payment_profile_id = get_post_meta( $invoice->get_id(), 'getpaid_authorizenet_profile_id', true );
621
        $customer_profile   = get_user_meta( $invoice->get_user_id(), $this->get_customer_profile_meta_name( $invoice ), true );
622
		$result             = $this->charge_customer_payment_profile( $customer_profile, $payment_profile_id, $invoice );
0 ignored issues
show
Bug introduced by
It seems like $payment_profile_id can also be of type false; however, parameter $payment_profile_id of GetPaid_Authorize_Net_Ga...tomer_payment_profile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

622
		$result             = $this->charge_customer_payment_profile( $customer_profile, /** @scrutinizer ignore-type */ $payment_profile_id, $invoice );
Loading history...
Bug introduced by
It seems like $customer_profile can also be of type false; however, parameter $customer_profile_id of GetPaid_Authorize_Net_Ga...tomer_payment_profile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

622
		$result             = $this->charge_customer_payment_profile( /** @scrutinizer ignore-type */ $customer_profile, $payment_profile_id, $invoice );
Loading history...
623
624
		// Do we have an error?
625
		if ( is_wp_error( $result ) ) {
626
			wpinv_set_error( $result->get_error_code(), $result->get_error_message() );
627
			wpinv_send_back_to_checkout( $invoice );
628
		}
629
630
		// Process the response.
631
		$this->process_charge_response( $result, $invoice );
0 ignored issues
show
Bug introduced by
$result of type WP_Error is incompatible with the type stdClass expected by parameter $result of GetPaid_Authorize_Net_Ga...ocess_charge_response(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

631
		$this->process_charge_response( /** @scrutinizer ignore-type */ $result, $invoice );
Loading history...
632
633
		if ( wpinv_get_errors() ) {
634
			wpinv_send_back_to_checkout( $invoice );
635
		}
636
637
	}
638
639
    /**
640
	 * Processes recurring payments.
641
	 *
642
     * @param WPInv_Invoice $invoice Invoice.
643
     * @param WPInv_Subscription[]|WPInv_Subscription $subscriptions Subscriptions.
644
	 */
645
	public function process_subscription( $invoice, $subscriptions ) {
646
647
        // Check if there is an initial amount to charge.
648
        if ( (float) $invoice->get_total() > 0 ) {
649
			$this->process_initial_payment( $invoice );
650
        }
651
652
        // Activate the subscriptions.
653
        $subscriptions = is_array( $subscriptions ) ? $subscriptions : array( $subscriptions );
654
655
        foreach ( $subscriptions as $subscription ) {
656
            if ( $subscription->exists() ) {
657
                $duration = strtotime( $subscription->get_expiration() ) - strtotime( $subscription->get_date_created() );
658
                $expiry   = date( 'Y-m-d H:i:s', ( current_time( 'timestamp' ) + $duration ) );
659
660
                $subscription->set_next_renewal_date( $expiry );
661
                $subscription->set_date_created( current_time( 'mysql' ) );
662
                $subscription->set_profile_id( $invoice->generate_key( 'authnet_sub_' . $invoice->get_id() . '_' . $subscription->get_id() ) );
663
                $subscription->activate();
664
            }
665
        }
666
667
		// Redirect to the success page.
668
        wpinv_send_to_success_page( array( 'invoice_key' => $invoice->get_key() ) );
669
670
    }
671
672
	/**
673
	 * (Maybe) renews an authorize.net subscription profile.
674
	 *
675
	 *
676
	 * @param bool $should_expire
677
     * @param WPInv_Subscription $subscription
678
	 */
679
	public function maybe_renew_subscription( $should_expire, $subscription ) {
680
681
        // Ensure its our subscription && it's active.
682
        if ( $this->id != $subscription->get_gateway() || ! $subscription->has_status( 'active trialling' ) ) {
683
            return $should_expire;
684
        }
685
686
        // If this is the last renewal, complete the subscription.
687
        if ( $subscription->is_last_renewal() ) {
688
            $subscription->complete();
689
            return false;
690
        }
691
692
        $this->renew_subscription( $subscription );
693
694
        return false;
695
696
	}
697
698
    /**
699
	 * Renews a subscription.
700
	 *
701
     * @param WPInv_Subscription $subscription
702
	 */
703
	public function renew_subscription( $subscription ) {
704
705
		// Generate the renewal invoice.
706
		$new_invoice = $subscription->create_payment();
707
		$old_invoice = $subscription->get_parent_payment();
708
709
        if ( empty( $new_invoice ) ) {
710
            $old_invoice->add_note( __( 'Error generating a renewal invoice.', 'invoicing' ), false, false, false );
711
            $subscription->failing();
712
            return;
713
        }
714
715
        // Charge the payment method.
716
		$payment_profile_id = get_post_meta( $old_invoice->get_id(), 'getpaid_authorizenet_profile_id', true );
717
		$customer_profile   = get_user_meta( $old_invoice->get_user_id(), $this->get_customer_profile_meta_name( $old_invoice ), true );
718
		$result             = $this->charge_customer_payment_profile( $customer_profile, $payment_profile_id, $new_invoice );
0 ignored issues
show
Bug introduced by
It seems like $customer_profile can also be of type false; however, parameter $customer_profile_id of GetPaid_Authorize_Net_Ga...tomer_payment_profile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

718
		$result             = $this->charge_customer_payment_profile( /** @scrutinizer ignore-type */ $customer_profile, $payment_profile_id, $new_invoice );
Loading history...
Bug introduced by
It seems like $payment_profile_id can also be of type false; however, parameter $payment_profile_id of GetPaid_Authorize_Net_Ga...tomer_payment_profile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

718
		$result             = $this->charge_customer_payment_profile( $customer_profile, /** @scrutinizer ignore-type */ $payment_profile_id, $new_invoice );
Loading history...
719
720
		// Do we have an error?
721
		if ( is_wp_error( $result ) ) {
722
723
			$old_invoice->add_note(
724
				sprintf( __( 'Error renewing subscription : ( %s ).', 'invoicing' ), $result->get_error_message() ),
725
				true,
726
				false,
727
				true
728
			);
729
			$subscription->failing();
730
			return;
731
732
		}
733
734
		// Process the response.
735
		$this->process_charge_response( $result, $new_invoice );
0 ignored issues
show
Bug introduced by
$result of type WP_Error is incompatible with the type stdClass expected by parameter $result of GetPaid_Authorize_Net_Ga...ocess_charge_response(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

735
		$this->process_charge_response( /** @scrutinizer ignore-type */ $result, $new_invoice );
Loading history...
736
737
		if ( wpinv_get_errors() ) {
738
739
			$old_invoice->add_note(
740
				sprintf( __( 'Error renewing subscription : ( %s ).', 'invoicing' ), getpaid_get_errors_html() ),
741
				true,
742
				false,
743
				true
744
			);
745
			$subscription->failing();
746
			return;
747
748
        }
749
750
        $subscription->add_payment( array(), $new_invoice );
751
        $subscription->renew();
752
    }
753
754
    /**
755
	 * Processes invoice addons.
756
	 *
757
	 * @param WPInv_Invoice $invoice
758
	 * @param GetPaid_Form_Item[] $items
759
	 * @return WPInv_Invoice
760
	 */
761
	public function process_addons( $invoice, $items ) {
762
763
        global $getpaid_authorize_addons;
764
765
        $getpaid_authorize_addons = array();
766
        foreach ( $items as $item ) {
767
768
            if ( is_null( $invoice->get_item( $item->get_id() ) ) && ! is_wp_error( $invoice->add_item( $item ) ) ) {
769
                $getpaid_authorize_addons[] = $item;
770
            }
771
772
        }
773
774
        if ( empty( $getpaid_authorize_addons ) ) {
775
            return;
776
        }
777
778
        $invoice->recalculate_total();
779
780
        $payment_profile_id = get_post_meta( $invoice->get_id(), 'getpaid_authorizenet_profile_id', true );
781
		$customer_profile   = get_user_meta( $invoice->get_user_id(), $this->get_customer_profile_meta_name( $invoice ), true );
782
783
        add_filter( 'getpaid_authorizenet_charge_customer_payment_profile_args', array( $this, 'filter_addons_request' ), 10, 2 );
784
        $result = $this->charge_customer_payment_profile( $customer_profile, $payment_profile_id, $invoice );
0 ignored issues
show
Bug introduced by
It seems like $payment_profile_id can also be of type false; however, parameter $payment_profile_id of GetPaid_Authorize_Net_Ga...tomer_payment_profile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

784
        $result = $this->charge_customer_payment_profile( $customer_profile, /** @scrutinizer ignore-type */ $payment_profile_id, $invoice );
Loading history...
Bug introduced by
It seems like $customer_profile can also be of type false; however, parameter $customer_profile_id of GetPaid_Authorize_Net_Ga...tomer_payment_profile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

784
        $result = $this->charge_customer_payment_profile( /** @scrutinizer ignore-type */ $customer_profile, $payment_profile_id, $invoice );
Loading history...
785
        remove_filter( 'getpaid_authorizenet_charge_customer_payment_profile_args', array( $this, 'filter_addons_request' ) );
786
787
        if ( is_wp_error( $result ) ) {
788
            wpinv_set_error( $result->get_error_code(), $result->get_error_message() );
789
            return;
790
        }
791
792
        $invoice->save();
793
    }
794
795
    /**
796
	 * Processes invoice addons.
797
	 *
798
     * @param array $args
799
	 * @return array
800
	 */
801
    public function filter_addons_request( $args ) {
802
803
        global $getpaid_authorize_addons;
804
        $total = 0;
805
806
        foreach ( $getpaid_authorize_addons as $addon ) {
807
            $total += $addon->get_sub_total();
808
        }
809
810
        $args['createTransactionRequest']['transactionRequest']['amount'] = $total;
811
812
        if ( isset( $args['createTransactionRequest']['transactionRequest']['tax'] ) ) {
813
            unset( $args['createTransactionRequest']['transactionRequest']['tax'] );
814
        }
815
816
        return $args;
817
818
    }
819
820
    /**
821
     * Displays a notice on the checkout page if sandbox is enabled.
822
     */
823
    public function sandbox_notice() {
824
825
        return sprintf(
826
            __( 'SANDBOX ENABLED. You can use sandbox testing details only. See the %sAuthorize.NET Sandbox Testing Guide%s for more details.', 'invoicing' ),
827
            '<a href="https://developer.authorize.net/hello_world/testing_guide.html">',
828
            '</a>'
829
        );
830
831
    }
832
833
    /**
834
	 * Filters the gateway settings.
835
	 *
836
	 * @param array $admin_settings
837
	 */
838
	public function admin_settings( $admin_settings ) {
839
840
        $currencies = sprintf(
841
            __( 'Supported Currencies: %s', 'invoicing' ),
842
            implode( ', ', $this->currencies )
843
        );
844
845
        $admin_settings['authorizenet_active']['desc'] .= " ($currencies)";
846
        $admin_settings['authorizenet_desc']['std']     = __( 'Pay securely using your credit or debit card.', 'invoicing' );
847
848
        $admin_settings['authorizenet_login_id'] = array(
849
            'type' => 'text',
850
            'id'   => 'authorizenet_login_id',
851
            'name' => __( 'API Login ID', 'invoicing' ),
852
            'desc' => '<a href="https://support.authorize.net/s/article/How-do-I-obtain-my-API-Login-ID-and-Transaction-Key"><em>' . __( 'How do I obtain my API Login ID and Transaction Key?', 'invoicing' ) . '</em></a>',
853
        );
854
855
        $admin_settings['authorizenet_transaction_key'] = array(
856
            'type' => 'text',
857
            'id'   => 'authorizenet_transaction_key',
858
            'name' => __( 'Transaction Key', 'invoicing' ),
859
        );
860
861
        $admin_settings['authorizenet_signature_key'] = array(
862
            'type' => 'text',
863
            'id'   => 'authorizenet_signature_key',
864
            'name' => __( 'Signature Key', 'invoicing' ),
865
            'desc' => '<a href="https://support.authorize.net/s/article/What-is-a-Signature-Key"><em>' . __( 'Learn more.', 'invoicing' ) . '</em></a>',
866
        );
867
868
        $admin_settings['authorizenet_ipn_url'] = array(
869
            'type'     => 'ipn_url',
870
            'id'       => 'authorizenet_ipn_url',
871
            'name'     => __( 'Webhook URL', 'invoicing' ),
872
            'std'      => $this->notify_url,
873
            'desc'     => __( 'Create a new webhook using this URL as the endpoint URL and set it to receive all payment events.', 'invoicing' ) . ' <a href="https://support.authorize.net/s/article/How-do-I-add-edit-Webhook-notification-end-points"><em>' . __( 'Learn more.', 'invoicing' ) . '</em></a>',
874
            'custom'   => 'authorizenet',
875
            'readonly' => true,
876
        );
877
878
		return $admin_settings;
879
	}
880
881
}
882