Passed
Branch master (50908e)
by Stiofan
07:01
created

wpinv_process_authorizenet_payment()   F

Complexity

Conditions 30
Paths > 20000

Size

Total Lines 166
Code Lines 116

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 30
eloc 116
c 3
b 0
f 0
nc 1132674
nop 1
dl 0
loc 166
rs 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
// Exit if accessed directly
3
if ( ! defined( 'ABSPATH' ) ) exit;
4
5
add_filter( 'wpinv_authorizenet_support_subscription', '__return_true' );
6
7
function wpinv_authorizenet_cc_form( $invoice_id ) {
8
    $invoice = wpinv_get_invoice( $invoice_id );
9
    $cc_owner = !empty( $invoice ) ? esc_attr( $invoice->get_user_full_name() ) : '';
10
    ?>
11
    <div id="authorizenet_cc_form" class="form-horizontal wpi-cc-form panel panel-default">
12
        <div class="panel-heading"><h3 class="panel-title"><?php _e( 'Card Details', 'invoicing' ) ;?></h3></div>
13
        <div class="panel-body">
14
            <div class="form-group required">
15
              <label for="auth-input-cc-owner" class="col-sm-3 control-label"><?php _e( 'Card Owner', 'invoicing' ) ;?></label>
16
              <div class="col-sm-5">
17
                <input type="text" class="form-control" id="auth-input-cc-owner" placeholder="<?php esc_attr_e( 'Card Owner', 'invoicing' ) ;?>" value="<?php echo $cc_owner;?>" name="authorizenet[cc_owner]">
18
              </div>
19
            </div>
20
            <div class="form-group required">
21
              <label for="auth-input-cc-number" class="col-sm-3 control-label"><?php _e( 'Card Number', 'invoicing' ) ;?></label>
22
              <div class="col-sm-5">
23
                <input type="text" class="form-control" id="auth-input-cc-number" placeholder="<?php esc_attr_e( 'Card Number', 'invoicing' ) ;?>" value="" name="authorizenet[cc_number]">
24
              </div>
25
            </div>
26
            <div class="form-group required">
27
              <label for="auth-input-cc-expire-date" class="col-sm-3 control-label"><?php _e( 'Card Expiry Date', 'invoicing' ) ;?></label>
28
              <div class="col-sm-2">
29
                <select class="form-control" id="auth-input-cc-expire-date" name="authorizenet[cc_expire_month]">
30
                    <?php for ( $i = 1; $i <= 12; $i++ ) { $value = str_pad( $i, 2, '0', STR_PAD_LEFT ); ?>
31
                    <option value="<?php echo $value;?>"><?php echo $value;?></option>
32
                    <?php } ?>
33
                </select>
34
               </div>
35
               <div class="col-sm-3">
36
                <select class="form-control" name="authorizenet[cc_expire_year]">
37
                    <?php $year = date( 'Y' ); for ( $i = $year; $i <= ( $year + 10 ); $i++ ) { ?>
38
                    <option value="<?php echo $i;?>"><?php echo $i;?></option>
39
                    <?php } ?>
40
                </select>
41
              </div>
42
            </div>
43
            <div class="form-group required">
44
              <label for="auth-input-cc-cvv2" class="col-sm-3 control-label"><?php _e( 'Card Security Code (CVV2)', 'invoicing' ) ;?></label>
45
              <div class="col-sm-5">
46
                <input type="text" class="form-control" id="auth-input-cc-cvv2" placeholder="<?php esc_attr_e( 'Card Security Code (CVV2)', 'invoicing' ) ;?>" value="" name="authorizenet[cc_cvv2]"">
47
              </div>
48
            </div>
49
      </div>
50
    </div>
51
    <?php
52
}
53
add_action( 'wpinv_authorizenet_cc_form', 'wpinv_authorizenet_cc_form', 10, 1 );
54
55
function wpinv_process_authorizenet_payment( $purchase_data ) {
56
    if( ! wp_verify_nonce( $purchase_data['gateway_nonce'], 'wpi-gateway' ) ) {
57
        wp_die( __( 'Nonce verification has failed', 'invoicing' ), __( 'Error', 'invoicing' ), array( 'response' => 403 ) );
58
    }
59
60
    // Collect payment data
61
    $payment_data = array(
62
        'price'         => $purchase_data['price'],
63
        'date'          => $purchase_data['date'],
64
        'user_email'    => $purchase_data['user_email'],
65
        'invoice_key'   => $purchase_data['invoice_key'],
66
        'currency'      => wpinv_get_currency(),
67
        'items'         => $purchase_data['items'],
68
        'user_info'     => $purchase_data['user_info'],
69
        'cart_details'  => $purchase_data['cart_details'],
70
        'gateway'       => 'authorizenet',
71
        'status'        => 'wpi-pending'
72
    );
73
74
    // Record the pending payment
75
    $invoice = wpinv_get_invoice( $purchase_data['invoice_id'] );
76
77
    if ( !empty( $invoice ) ) {
78
        $authorizenet_card  = !empty( $_POST['authorizenet'] ) ? $_POST['authorizenet'] : array();
79
        $card_defaults      = array(
80
            'cc_owner'          => $invoice->get_user_full_name(),
81
            'cc_number'         => false,
82
            'cc_expire_month'   => false,
83
            'cc_expire_year'    => false,
84
            'cc_cvv2'           => false,
85
        );
86
        $authorizenet_card = wp_parse_args( $authorizenet_card, $card_defaults );
0 ignored issues
show
Security Variable Injection introduced by
$authorizenet_card can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and ! empty($_POST['authorizenet']) ? $_POST['authorizenet'] : array() is assigned to $authorizenet_card
    in includes/gateways/authorizenet.php on line 78

Used in variable context

  1. wp_parse_args() is called
    in includes/gateways/authorizenet.php on line 86
  2. Enters via parameter $args
    in wordpress/wp-includes/functions.php on line 4294
  3. wp_parse_str() is called
    in wordpress/wp-includes/functions.php on line 4300
  4. Enters via parameter $string
    in wordpress/wp-includes/formatting.php on line 4865
  5. parse_str() is called
    in wordpress/wp-includes/formatting.php on line 4866

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
87
88
        if ( empty( $authorizenet_card['cc_owner'] ) ) {
89
            wpinv_set_error( 'empty_card_name', __( 'You must enter the name on your card!', 'invoicing'));
90
        }
91
        if ( empty( $authorizenet_card['cc_number'] ) ) {
92
            wpinv_set_error( 'empty_card', __( 'You must enter a card number!', 'invoicing'));
93
        }
94
        if ( empty( $authorizenet_card['cc_expire_month'] ) ) {
95
            wpinv_set_error( 'empty_month', __( 'You must enter an card expiration month!', 'invoicing'));
96
        }
97
        if ( empty( $authorizenet_card['cc_expire_year'] ) ) {
98
            wpinv_set_error( 'empty_year', __( 'You must enter an card expiration year!', 'invoicing'));
99
        }
100
        if ( empty( $authorizenet_card['cc_cvv2'] ) ) {
101
            wpinv_set_error( 'empty_cvv2', __( 'You must enter a valid CVV2!', 'invoicing' ) );
102
        }
103
104
        $errors = wpinv_get_errors();
105
106
        if ( empty( $errors ) ) {
107
            $invoice_id = $invoice->ID;
108
            $quantities_enabled = wpinv_item_quantities_enabled();
109
            $use_taxes          = wpinv_use_taxes();
110
111
            $authorizeAIM = wpinv_authorizenet_AIM();
112
            $authorizeAIM->first_name       = wpinv_utf8_substr( $invoice->get_first_name(), 0, 50 );
0 ignored issues
show
Bug Best Practice introduced by
The property first_name does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
113
            $authorizeAIM->last_name        = wpinv_utf8_substr( $invoice->get_last_name(), 0, 50 );
0 ignored issues
show
Bug Best Practice introduced by
The property last_name does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
114
            $authorizeAIM->company          = wpinv_utf8_substr( $invoice->company, 0, 50 );
0 ignored issues
show
Bug Best Practice introduced by
The property company does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
115
            $authorizeAIM->address          = wpinv_utf8_substr( wp_strip_all_tags( $invoice->get_address(), true ), 0, 60 );
0 ignored issues
show
Bug Best Practice introduced by
The property address does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
116
            $authorizeAIM->city             = wpinv_utf8_substr( $invoice->city, 0, 40 );
0 ignored issues
show
Bug Best Practice introduced by
The property city does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
117
            $authorizeAIM->state            = wpinv_utf8_substr( $invoice->state, 0, 40 );
0 ignored issues
show
Bug Best Practice introduced by
The property state does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
118
            $authorizeAIM->zip              = wpinv_utf8_substr( $invoice->zip, 0, 40 );
0 ignored issues
show
Bug Best Practice introduced by
The property zip does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
119
            $authorizeAIM->country          = wpinv_utf8_substr( $invoice->country, 0, 60 );
0 ignored issues
show
Bug Best Practice introduced by
The property country does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
120
            $authorizeAIM->phone            = wpinv_utf8_substr( $invoice->phone, 0, 25 );
0 ignored issues
show
Bug Best Practice introduced by
The property phone does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
121
            $authorizeAIM->email            = wpinv_utf8_substr( $invoice->get_email(), 0, 255 );
0 ignored issues
show
Bug Best Practice introduced by
The property email does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
122
            $authorizeAIM->amount           = wpinv_sanitize_amount( $invoice->get_total() );
0 ignored issues
show
Bug Best Practice introduced by
The property amount does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
123
            $authorizeAIM->card_num         = str_replace( ' ', '', sanitize_text_field( $authorizenet_card['cc_number'] ) );
0 ignored issues
show
Bug Best Practice introduced by
The property card_num does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
124
            $authorizeAIM->exp_date         = sanitize_text_field( $authorizenet_card['cc_expire_month'] ) . sanitize_text_field( $authorizenet_card['cc_expire_year'] );
0 ignored issues
show
Bug Best Practice introduced by
The property exp_date does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
125
            $authorizeAIM->card_code        = sanitize_text_field( $authorizenet_card['cc_cvv2'] );
0 ignored issues
show
Bug Best Practice introduced by
The property card_code does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
126
            $authorizeAIM->invoice_num      = $invoice->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property invoice_num does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
127
128
            $item_desc = array();
129
            foreach ( $invoice->get_cart_details() as $item ) {            
130
                $quantity       = $quantities_enabled && !empty( $item['quantity'] ) && $item['quantity'] > 0 ? $item['quantity'] : 1;
131
                $item_name      = wpinv_utf8_substr( $item['name'], 0, 31 );
132
                $item_desc[]    = $item_name . ' (' . $quantity . 'x ' . wpinv_price( wpinv_format_amount( $item['item_price'] ) ) . ')';
133
134
                $authorizeAIM->addLineItem( $item['id'], $item_name, '', $quantity, $item['item_price'], ( $use_taxes && !empty( $item['tax'] ) && $item['tax'] > 0 ? 'Y' : 'N' ) );
135
            }
136
137
            $item_desc = '#' . $invoice->get_number() . ': ' . implode( ', ', $item_desc );
138
139
            if ( $use_taxes && $invoice->get_tax() > 0 ) {
140
                $authorizeAIM->tax  = $invoice->get_tax();
0 ignored issues
show
Bug Best Practice introduced by
The property tax does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
141
142
                $item_desc .= ', ' . wp_sprintf( __( 'Tax: %s', 'invoicing' ), $invoice->get_tax( true ) );
143
            }
144
145
            if ( $invoice->get_discount() > 0 ) {
146
                $item_desc .= ', ' . wp_sprintf( __( 'Discount: %s', 'invoicing' ), $invoice->get_discount( true ) );
147
            }
148
149
            $item_description = wpinv_utf8_substr( $item_desc, 0, 255 );
0 ignored issues
show
Unused Code introduced by
The assignment to $item_description is dead and can be removed.
Loading history...
150
            $item_description = html_entity_decode( $item_desc , ENT_QUOTES, 'UTF-8' );
151
152
            $authorizeAIM->description  = wpinv_utf8_substr( $item_description, 0, 255 );
0 ignored issues
show
Bug Best Practice introduced by
The property description does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
153
154
            $is_recurring = $invoice->is_recurring(); // Recurring payment.
155
156
            if ( $is_recurring ) {
157
                $authorizeAIM->recurring_billing = true;
0 ignored issues
show
Bug Best Practice introduced by
The property recurring_billing does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
158
            }
159
160
            try {
161
162
                if ( $is_recurring ) {
163
                    $trx_type = wpinv_get_option('authorizenet_transaction_type_recurring', 'authorize_only');
164
                    if('authorize_capture' == $trx_type){
165
                        $response = $authorizeAIM->authorizeAndCapture();
166
                    } else {
167
                        $response = $authorizeAIM->authorizeOnly();
168
                    }
169
                } else {
170
                    $trx_type = wpinv_get_option('authorizenet_transaction_type', 'authorize_capture');
171
                    if('authorize_capture' == $trx_type){
172
                        $response = $authorizeAIM->authorizeAndCapture();
173
                    } else {
174
                        $response = $authorizeAIM->authorizeOnly();
175
                    }
176
                }
177
178
                if ( $response->approved || $response->held ) {
179
                    if ( $response->approved ) {
180
                        wpinv_update_payment_status( $invoice_id, 'publish' );
181
                    }
182
                    wpinv_set_payment_transaction_id( $invoice_id, $response->transaction_id );
183
184
                    wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'Authorize.Net payment response: %s', 'invoicing' ), $response->response_reason_text ), '', '', true );
185
                    wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'Authorize.Net payment: Transaction ID %s, Transaction Type %s, Authorization Code %s', 'invoicing' ), $response->transaction_id, strtoupper( $response->transaction_type ), $response->authorization_code ), '', '', true );
186
187
                    do_action( 'wpinv_authorizenet_handle_response', $response, $invoice, $authorizenet_card );
188
189
                    wpinv_clear_errors();
190
                    wpinv_empty_cart();
191
192
                    wpinv_send_to_success_page( array( 'invoice_key' => $invoice->get_key() ) );
193
                } else {
194
                    if ( !empty( $response->response_reason_text ) ) {
195
                        $error = __( $response->response_reason_text, 'invoicing' );
196
                    } else if ( !empty( $response->error_message ) ) {
197
                        $error = __( $response->error_message, 'invoicing' );
198
                    } else {
199
                        $error = wp_sprintf( __( 'Error data: %s', 'invoicing' ), print_r( $response, true ) );
200
                    } 
201
202
                    $error = wp_sprintf( __( 'Authorize.Net payment error occurred. %s', 'invoicing' ), $error );
203
204
                    wpinv_set_error( 'payment_error', $error );
205
                    wpinv_record_gateway_error( $error, $response );
206
                    wpinv_insert_payment_note( $invoice_id, $error, '', '', true );
207
208
                    wpinv_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['wpi-gateway'] );
209
                }
210
            } catch ( AuthorizeNetException $e ) {
211
                wpinv_set_error( 'request_error', $e->getMessage() );
212
                wpinv_record_gateway_error( wp_sprintf( __( 'Authorize.Net payment error occurred. %s', 'invoicing' ), $e->getMessage() ) );
213
                wpinv_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['wpi-gateway'] );
214
            }
215
        } else {
216
            wpinv_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['wpi-gateway'] );
217
        }
218
    } else {
219
        wpinv_record_gateway_error( wp_sprintf( __( 'Authorize.Net payment error occurred. Payment creation failed while processing a Authorize.Net payment. Payment data: %s', 'invoicing' ), print_r( $payment_data, true ) ), $invoice );
220
        wpinv_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['wpi-gateway'] );
221
    }
222
}
223
add_action( 'wpinv_gateway_authorizenet', 'wpinv_process_authorizenet_payment' );
224
225
function wpinv_authorizenet_cancel_subscription( $subscription_id = '' ) {
226
    if ( empty( $subscription_id ) ) {
227
        return false;
228
    }
229
230
    try {
231
        $authnetXML = wpinv_authorizenet_XML();
232
        $authnetXML->ARBCancelSubscriptionRequest( array( 'subscriptionId' => $subscription_id ) );
0 ignored issues
show
Bug introduced by
The method ARBCancelSubscriptionRequest() does not exist on AuthnetXML. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

232
        $authnetXML->/** @scrutinizer ignore-call */ 
233
                     ARBCancelSubscriptionRequest( array( 'subscriptionId' => $subscription_id ) );
Loading history...
233
        return $authnetXML->isSuccessful();
234
    } catch( Exception $e ) {
235
        wpinv_error_log( $e->getMessage(), __( 'Authorize.Net cancel subscription', 'invoicing' ) );
236
    }
237
238
    return false;
239
}
240
241
function wpinv_recurring_cancel_authorizenet_subscription( $subscription, $valid = false ) {
242
    if ( ! empty( $valid ) && ! empty( $subscription->profile_id ) ) {
243
        return wpinv_authorizenet_cancel_subscription( $subscription->profile_id );
244
    }
245
    
246
    return false;
247
}
248
add_action( 'wpinv_recurring_cancel_authorizenet_subscription', 'wpinv_recurring_cancel_authorizenet_subscription', 10, 2 );
249
250
function wpinv_authorizenet_valid_ipn( $md5_hash, $transaction_id, $amount ) {
251
    $authorizenet_md5_hash = wpinv_get_option( 'authorizenet_md5_hash' );
252
    if ( empty( $authorizenet_md5_hash ) ) {
253
        return true;
254
    }
255
256
    $compare_md5 = strtoupper( md5( $authorizenet_md5_hash . $transaction_id . $amount ) );
257
258
    return hash_equals( $compare_md5, $md5_hash );
259
}
260
261
function wpinv_authorizenet_AIM() {
262
    if ( !class_exists( 'AuthorizeNetException' ) ) {
263
        require_once plugin_dir_path( WPINV_PLUGIN_FILE ) . 'includes/gateways/authorizenet/anet_php_sdk/AuthorizeNet.php';
264
    }
265
266
    $authorizeAIM = new AuthorizeNetAIM( wpinv_get_option( 'authorizenet_login_id' ), wpinv_get_option( 'authorizenet_transaction_key' ) );
0 ignored issues
show
Bug introduced by
It seems like wpinv_get_option('authorizenet_transaction_key') can also be of type false; however, parameter $transaction_key of AuthorizeNetAIM::__construct() 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

266
    $authorizeAIM = new AuthorizeNetAIM( wpinv_get_option( 'authorizenet_login_id' ), /** @scrutinizer ignore-type */ wpinv_get_option( 'authorizenet_transaction_key' ) );
Loading history...
Bug introduced by
It seems like wpinv_get_option('authorizenet_login_id') can also be of type false; however, parameter $api_login_id of AuthorizeNetAIM::__construct() 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

266
    $authorizeAIM = new AuthorizeNetAIM( /** @scrutinizer ignore-type */ wpinv_get_option( 'authorizenet_login_id' ), wpinv_get_option( 'authorizenet_transaction_key' ) );
Loading history...
267
268
    if ( wpinv_is_test_mode( 'authorizenet' ) ) {
269
        $authorizeAIM->setSandbox( true );
270
    } else {
271
        $authorizeAIM->setSandbox( false );
272
    }
273
274
    $authorizeAIM->customer_ip = wpinv_get_ip();
0 ignored issues
show
Bug Best Practice introduced by
The property customer_ip does not exist on AuthorizeNetAIM. Since you implemented __set, consider adding a @property annotation.
Loading history...
275
276
    return $authorizeAIM;
277
}
278
279
function wpinv_authorizenet_XML() {
280
    if ( !class_exists( 'AuthnetXML' ) ) {
281
        require_once plugin_dir_path( WPINV_PLUGIN_FILE ) . 'includes/gateways/authorizenet/Authorize.Net-XML/AuthnetXML.class.php';
282
    }
283
    
284
    $authnetXML = new AuthnetXML( wpinv_get_option( 'authorizenet_login_id' ), wpinv_get_option( 'authorizenet_transaction_key' ), (bool)wpinv_is_test_mode( 'authorizenet' ) );
285
    
286
    return $authnetXML;
287
}
288
289
function wpinv_authorizenet_handle_response( $response, $invoice, $card_info = array() ) {
290
    if ( empty( $response ) || empty( $invoice ) ) {
291
        return false;
292
    }
293
294
    if ( $invoice->is_recurring() && !empty( $response->approved ) ) {
295
        $subscription = wpinv_authorizenet_create_new_subscription( $invoice, $response, $card_info );
296
        $success = false;
297
        if ( wpinv_is_test_mode( 'authorizenet' ) ) {
298
            $success = true;
299
        } else {
300
            $success = $subscription->isSuccessful();
301
        }
302
303
        if ( !empty( $subscription ) && $success ) {
304
            do_action( 'wpinv_recurring_post_create_subscription', $subscription, $invoice, 'authorizenet' );
305
306
            wpinv_authorizenet_subscription_record_signup( $subscription, $invoice );
307
308
            do_action( 'wpinv_recurring_post_record_signup', $subscription, $invoice, 'authorizenet' );
309
        } else {
310
            if ( isset( $subscription->messages->message ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property messages does not exist on AuthnetXML. Since you implemented __get, consider adding a @property annotation.
Loading history...
311
                $error = $subscription->messages->message->code . ': ' . $subscription->messages->message->text;
312
                wpinv_set_error( 'wpinv_authorize_recurring_error', $error, 'invoicing' );
0 ignored issues
show
Unused Code introduced by
The call to wpinv_set_error() has too many arguments starting with 'invoicing'. ( Ignorable by Annotation )

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

312
                /** @scrutinizer ignore-call */ 
313
                wpinv_set_error( 'wpinv_authorize_recurring_error', $error, 'invoicing' );

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...
313
            } else {
314
                $error = __( 'Your subscription cannot be created due to an error.', 'invoicing' );
315
                wpinv_set_error( 'wpinv_authorize_recurring_error', $error );
316
            }
317
318
            wpinv_record_gateway_error( $error, $subscription );
319
320
            wpinv_insert_payment_note( $invoice->ID, wp_sprintf( __( 'Authorize.Net subscription error occurred. %s', 'invoicing' ), $error ), '', '', true );
321
        }
322
    }
323
}
324
add_action( 'wpinv_authorizenet_handle_response', 'wpinv_authorizenet_handle_response', 10, 3 );
325
326
function wpinv_authorizenet_create_new_subscription( $invoice, $response = array(), $card_info = array() ) {
327
    if ( empty( $invoice ) ) {
328
        return false;
329
    }
330
331
    $params = wpinv_authorizenet_generate_subscription_params( $invoice, $card_info, $response );
332
333
    try {
334
        $authnetXML = wpinv_authorizenet_XML();
335
        $authnetXML->ARBCreateSubscriptionRequest( $params );
0 ignored issues
show
Bug introduced by
The method ARBCreateSubscriptionRequest() does not exist on AuthnetXML. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

335
        $authnetXML->/** @scrutinizer ignore-call */ 
336
                     ARBCreateSubscriptionRequest( $params );
Loading history...
336
    } catch( Exception $e ) {
337
        $authnetXML = array();
338
        wpinv_error_log( $e->getMessage(), __( 'Authorize.Net cancel subscription', 'invoicing' ) );
339
    }
340
341
    return $authnetXML;
342
}
343
344
function wpinv_authorizenet_generate_subscription_params( $invoice, $card_info = array(), $response = array() ) {
345
    if ( empty( $invoice ) ) {
346
        return false;
347
    }
348
349
    $subscription_item = $invoice->get_recurring( true );
350
    if ( empty( $subscription_item->ID ) ) {
351
        return false;
352
    }
353
354
    $item = $invoice->get_recurring( true );
355
356
    if ( empty( $item ) ) {
357
        $name = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $name is dead and can be removed.
Loading history...
358
    }
359
360
    if ( !( $name = $item->get_name() ) ) {
361
        $name = $item->post_name;
362
    }
363
364
    $card_details       = wpinv_authorizenet_generate_card_info( $card_info );
365
    $subscription_name  = $invoice->get_subscription_name();
366
    $initial_amount     = wpinv_round_amount( $invoice->get_total() );
367
    $recurring_amount   = wpinv_round_amount( $invoice->get_recurring_details( 'total' ) );
368
    $interval           = $subscription_item->get_recurring_interval();
369
    $period             = $subscription_item->get_recurring_period();
370
    $bill_times         = (int)$subscription_item->get_recurring_limit();
371
    $bill_times         = $bill_times > 0 ? $bill_times : 9999;
372
373
    $time_period        = wpinv_authorizenet_get_time_period( $interval, $period );
374
    $interval           = $time_period['interval'];
375
    $period             = $time_period['period'];
376
377
    $current_tz = date_default_timezone_get();
378
    date_default_timezone_set( 'America/Denver' ); // Set same timezone as Authorize's server (Mountain Time) to prevent conflicts.
379
    $today = date( 'Y-m-d' );
380
    date_default_timezone_set( $current_tz );
381
382
    $free_trial = $invoice->is_free_trial();
383
    if ( $free_trial && $subscription_item->has_free_trial() ) {
384
        $trial_interval    = $subscription_item->get_trial_interval();
0 ignored issues
show
Unused Code introduced by
The assignment to $trial_interval is dead and can be removed.
Loading history...
385
        $trial_period      = $subscription_item->get_trial_period( true );
0 ignored issues
show
Unused Code introduced by
The assignment to $trial_period is dead and can be removed.
Loading history...
386
    }
387
388
    $subscription = array();
389
    $subscription['name'] = $subscription_name;
390
391
    $subscription['paymentSchedule'] = array(
392
        'interval'         => array( 'length' => $interval, 'unit' => $period ),
393
        'startDate'        => $today,
394
        'totalOccurrences' => $bill_times,
395
        'trialOccurrences' => $free_trial || ( $initial_amount != $recurring_amount ) ? 1 : 0,
396
    );
397
398
    $subscription['amount'] = $recurring_amount;
399
    $subscription['trialAmount'] = $initial_amount;
400
    $subscription['payment'] = array( 'creditCard' => $card_details );
401
    $subscription['order'] = array( 'invoiceNumber' => $invoice->ID, 'description' => '#' . $invoice->get_number() );
402
    $subscription['customer'] = array( 'id' => $invoice->get_user_id(), 'email' => $invoice->get_email(), 'phoneNumber' => $invoice->phone );
403
404
    $subscription['billTo'] = array(
405
        'firstName' => $invoice->get_first_name(),
406
        'lastName'  => $invoice->get_last_name(),
407
        'company'   => $invoice->company,
408
        'address'   => wp_strip_all_tags( $invoice->get_address(), true ),
409
        'city'      => $invoice->city,
410
        'state'     => $invoice->state,
411
        'zip'       => $invoice->zip,
412
        'country'   => $invoice->country,
413
    );
414
415
    $params = array( 'subscription' => $subscription );
416
417
    return apply_filters( 'wpinv_authorizenet_generate_subscription_params', $params, $invoice, $card_info, $response );
418
}
419
420
function wpinv_authorizenet_generate_card_info( $card_info = array() ) {
421
    $card_defaults      = array(
422
        'cc_owner'          => null,
423
        'cc_number'         => null,
424
        'cc_expire_month'   => null,
425
        'cc_expire_year'    => null,
426
        'cc_cvv2'           => null,
427
    );
428
    $card_info = wp_parse_args( $card_info, $card_defaults );
429
430
    $card_details = array(
431
        'cardNumber'     => str_replace( ' ', '', sanitize_text_field( $card_info['cc_number'] ) ),
432
        'expirationDate' => sanitize_text_field( $card_info['cc_expire_month'] ) . sanitize_text_field( $card_info['cc_expire_year'] ),
433
        'cardCode'       => sanitize_text_field( $card_info['cc_cvv2'] ),
434
    );
435
436
    return $card_details;
437
}
438
439
function wpinv_authorizenet_subscription_record_signup( $subscription, $invoice ) {
440
    $parent_invoice_id = absint( $invoice->ID );
441
442
    if( empty( $parent_invoice_id ) ) {
443
        return;
444
    }
445
446
    $invoice = wpinv_get_invoice( $parent_invoice_id );
447
    if ( empty( $invoice ) ) {
448
        return;
449
    }
450
451
    $subscriptionId     = (array)$subscription->subscriptionId;
452
    $subscription_id    = !empty( $subscriptionId[0] ) ? $subscriptionId[0] : $parent_invoice_id;
453
454
    $subscription = wpinv_get_authorizenet_subscription( $subscription, $parent_invoice_id );
455
456
    if ( false === $subscription ) {
457
        return;
458
    }
459
460
    // Set payment to complete
461
    wpinv_update_payment_status( $subscription->parent_payment_id, 'publish' );
462
    sleep(1);
463
    wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'Authorize.Net Subscription ID: %s', 'invoicing' ) , $subscription_id ), '', '', true );
464
    update_post_meta($parent_invoice_id,'_wpinv_subscr_profile_id', $subscription_id);
465
466
    $status     = 'trialling' == $subscription->status ? 'trialling' : 'active';
467
    $diff_days  = absint( ( ( strtotime( $subscription->expiration ) - strtotime( $subscription->created ) ) / DAY_IN_SECONDS ) );
468
    $created    = date_i18n( 'Y-m-d H:i:s' );
469
    $expiration = date_i18n( 'Y-m-d 23:59:59', ( strtotime( $created ) + ( $diff_days * DAY_IN_SECONDS ) ) );
470
471
    // Retrieve pending subscription from database and update it's status to active and set proper profile ID
472
    $subscription->update( array( 'profile_id' => $subscription_id, 'status' => $status, 'created' => $created, 'expiration' => $expiration ) );
473
}
474
475
function wpinv_authorizenet_validate_checkout( $valid_data, $post ) {
0 ignored issues
show
Unused Code introduced by
The parameter $valid_data is not used and could be removed. ( Ignorable by Annotation )

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

475
function wpinv_authorizenet_validate_checkout( /** @scrutinizer ignore-unused */ $valid_data, $post ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
476
    if ( !empty( $post['wpi-gateway'] ) && $post['wpi-gateway'] == 'authorizenet' ) {
477
        $error = false;
478
        
479
        if ( empty( $post['authorizenet']['cc_owner'] ) ) {
480
            $error = true;
481
            wpinv_set_error( 'empty_card_name', __( 'You must enter the name on your card!', 'invoicing'));
482
        }
483
        if ( empty( $post['authorizenet']['cc_number'] ) ) {
484
            $error = true;
485
            wpinv_set_error( 'empty_card', __( 'You must enter a card number!', 'invoicing'));
486
        }
487
        if ( empty( $post['authorizenet']['cc_expire_month'] ) ) {
488
            $error = true;
489
            wpinv_set_error( 'empty_month', __( 'You must enter an card expiration month!', 'invoicing'));
490
        }
491
        if ( empty( $post['authorizenet']['cc_expire_year'] ) ) {
492
            $error = true;
493
            wpinv_set_error( 'empty_year', __( 'You must enter an card expiration year!', 'invoicing'));
494
        }
495
        if ( empty( $post['authorizenet']['cc_cvv2'] ) ) {
496
            $error = true;
497
            wpinv_set_error( 'empty_cvv2', __( 'You must enter a valid CVV2!', 'invoicing' ) );
498
        }
499
500
        if ( $error ) {
501
            return;
502
        }
503
504
        $invoice = wpinv_get_invoice_cart();
505
506
        if ( !empty( $invoice ) && $subscription_item = $invoice->get_recurring( true ) ) {
0 ignored issues
show
Unused Code introduced by
The assignment to $subscription_item is dead and can be removed.
Loading history...
507
            $subscription_item = $invoice->get_recurring( true );
508
509
            $interval   = $subscription_item->get_recurring_interval();
510
            $period     = $subscription_item->get_recurring_period();
511
512
            if ( $period == 'D' && ( $interval < 7 || $interval > 365 ) ) {
513
                wpinv_set_error( 'authorizenet_subscription_error', __( 'Interval Length must be a value from 7 through 365 for day based subscriptions.', 'invoicing' ) );
514
            }
515
        }
516
    }
517
}
518
add_action( 'wpinv_checkout_error_checks', 'wpinv_authorizenet_validate_checkout', 11, 2 );
519
520
function wpinv_authorizenet_get_time_period( $subscription_interval, $subscription_period ) {
521
    $subscription_interval = absint( $subscription_interval );
522
523
    switch( $subscription_period ) {
524
        case 'W':
525
        case 'week':
526
        case 'weeks':
527
            $interval = $subscription_interval * 7;
528
            $period   = 'days';
529
            break;
530
        case 'M':
531
        case 'month':
532
        case 'months':
533
            if ( $subscription_interval > 12 ) {
534
                $subscription_interval = 12;
535
            }
536
537
            $interval = $subscription_interval;
538
            $period   = 'months';
539
            
540
            if ( !( $subscription_interval === 1 || $subscription_interval === 2 || $subscription_interval === 3 || $subscription_interval === 6 || $subscription_interval === 12 ) ) {
541
                $interval = $subscription_interval * 30;
542
                $period   = 'days';
543
            }
544
            break;
545
        case 'Y':
546
        case 'year':
547
        case 'years':
548
            $interval = 12;
549
            $period   = 'months';
550
            break;
551
        default :
552
            $interval = $subscription_interval;
553
            $period   = 'days';
554
            break;
555
    }
556
557
    return compact( 'interval', 'period' );
558
}
559
560
function wpinv_authorizenet_process_ipn() {
561
    if ( !( !empty( $_REQUEST['wpi-gateway'] ) && $_REQUEST['wpi-gateway'] == 'authorizenet' ) ) {
562
        return;
563
    }
564
565
    $subscription_id = !empty( $_POST['x_subscription_id'] ) ? intval( $_POST['x_subscription_id'] ) : false;
566
567
    if ( $subscription_id ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subscription_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
568
        $response_code  = intval( $_POST['x_response_code'] );
569
        $reason_code    = intval( $_POST['x_response_reason_code'] );
570
571
        $subscription = new WPInv_Subscription( $subscription_id, true );
572
573
        if ( !$subscription->id ) {
574
            return;
575
        }
576
577
        if ( 1 == $response_code ) {
578
            // Approved
579
            $transaction_id = sanitize_text_field( $_POST['x_trans_id'] );
580
            $renewal_amount = sanitize_text_field( $_POST['x_amount'] );
581
582
            $args = array(
583
                'amount'         => $renewal_amount,
584
                'transaction_id' => $transaction_id,
585
                'gateway'        => 'authorizenet'
586
            );
587
588
            $subscription->add_payment( $args );
589
            $subscription->renew();
590
591
            do_action( 'wpinv_recurring_authorizenet_silent_post_payment', $subscription );
592
            do_action( 'wpinv_authorizenet_renewal_payment', $subscription );
593
        } else if ( 2 == $response_code ) {
594
            // Declined
595
            $subscription->failing();
596
            do_action( 'wpinv_authorizenet_renewal_payment_failed', $subscription );
597
            do_action( 'wpinv_authorizenet_renewal_error', $subscription );
598
        } else if ( 3 == $response_code || 8 == $reason_code ) {
599
            // An expired card
600
            $subscription->failing();
601
            do_action( 'wpinv_authorizenet_renewal_payment_failed', $subscription );
602
            do_action( 'wpinv_authorizenet_renewal_error', $subscription );
603
        } else {
604
            // Other Error
605
            do_action( 'wpinv_authorizenet_renewal_payment_error', $subscription );
606
        }
607
608
        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...
609
    }
610
}
611
add_action( 'wpinv_verify_authorizenet_ipn', 'wpinv_authorizenet_process_ipn' );
612
613
/**
614
 * Retrieve the subscription
615
 */
616
function wpinv_get_authorizenet_subscription( $subscription_data = array(), $invoice_id ) {
617
    $parent_invoice_id = absint( $invoice_id );
618
619
    if ( empty( $subscription_data ) ) {
620
        return false;
621
    }
622
623
    if ( empty( $parent_invoice_id ) ) {
624
        return false;
625
    }
626
627
    $invoice = wpinv_get_invoice( $parent_invoice_id );
628
    if ( empty( $invoice ) ) {
629
        return false;
630
    }
631
632
    $subscriptionId     = (array)$subscription_data->subscriptionId;
633
    $subscription_id    = !empty( $subscriptionId[0] ) ? $subscriptionId[0] : $parent_invoice_id;
634
635
    $subscription = new WPInv_Subscription( $subscription_id, true );
636
637
    if ( ! $subscription || $subscription->id < 1 ) {
0 ignored issues
show
introduced by
$subscription is of type WPInv_Subscription, thus it always evaluated to true.
Loading history...
638
        $subs_db      = new WPInv_Subscriptions_DB;
639
        $subs         = $subs_db->get_subscriptions( array( 'parent_payment_id' => $parent_invoice_id, 'number' => 1 ) );
640
        $subscription = reset( $subs );
641
642
        if ( $subscription && $subscription->id > 0 ) {
643
            // Update the profile ID so it is set for future renewals
644
            $subscription->update( array( 'profile_id' => sanitize_text_field( $subscription_id ) ) );
645
        } else {
646
            // No subscription found with a matching payment ID, bail
647
            return false;
648
        }
649
    }
650
651
    return $subscription;
652
}
653
654
function wpinv_is_authorizenet_valid_for_use() {
655
    return in_array( wpinv_get_currency(), apply_filters( 'wpinv_authorizenet_supported_currencies', array( 'AUD', 'CAD', 'CHF', 'DKK', 'EUR', 'GBP', 'JPY', 'NOK', 'NZD', 'PLN', 'SEK', 'USD', 'ZAR' ) ) );
656
}
657
function wpinv_check_authorizenet_currency_support( $gateway_list ) {
658
    if ( isset( $gateway_list['authorizenet'] ) && ! wpinv_is_authorizenet_valid_for_use() ) {
659
        unset( $gateway_list['authorizenet'] );
660
    }
661
    return $gateway_list;
662
}
663
add_filter( 'wpinv_enabled_payment_gateways', 'wpinv_check_authorizenet_currency_support', 10, 1 );
664
665
function wpinv_authorizenet_link_transaction_id( $transaction_id, $invoice_id, $invoice ) {
666
    if ( $transaction_id == $invoice_id ) {
667
        $link = $transaction_id;
668
    } else {
669
        if ( ! empty( $invoice ) && ! empty( $invoice->mode ) ) {
670
            $mode = $invoice->mode;
671
        } else {
672
            $mode = wpinv_is_test_mode( 'authorizenet' ) ? 'test' : 'live';
673
        }
674
675
        $url = $mode == 'test' ? 'https://sandbox.authorize.net/' : 'https://authorize.net/';
676
        $url .= 'ui/themes/sandbox/Transaction/TransactionReceipt.aspx?transid=' . $transaction_id;
677
678
        $link = '<a href="' . esc_url( $url ) . '" target="_blank">' . $transaction_id . '</a>';
679
    }
680
681
    return apply_filters( 'wpinv_authorizenet_link_payment_details_transaction_id', $link, $transaction_id, $invoice );
682
}
683
add_filter( 'wpinv_payment_details_transaction_id-authorizenet', 'wpinv_authorizenet_link_transaction_id', 10, 3 );
684
685
function wpinv_authorizenet_transaction_id_link( $transaction_id, $subscription ) {
686
    if ( ! empty( $transaction_id ) && ! empty( $subscription ) && ( $invoice_id = $subscription->get_original_payment_id() ) ) {
687
        $invoice = wpinv_get_invoice( $invoice_id );
688
689
        if ( ! empty( $invoice ) ) {
690
            return wpinv_authorizenet_link_transaction_id( $transaction_id, $invoice_id, $invoice );
691
        }        
692
    }
693
    
694
    return $transaction_id;
695
}
696
add_filter( 'wpinv_subscription_transaction_link_authorizenet', 'wpinv_authorizenet_transaction_id_link', 10, 2 );
697
698
function wpinv_authorizenet_profile_id_link( $profile_id, $subscription ) {
699
    $link = $profile_id;
700
701
    if ( ! empty( $profile_id ) && ! empty( $subscription ) && ( $invoice_id = $subscription->get_original_payment_id() ) ) {
702
        $invoice = wpinv_get_invoice( $invoice_id );
703
704
        if ( ! empty( $invoice ) && ! empty( $invoice->mode ) ) {
705
            $mode = $invoice->mode;
706
        } else {
707
            $mode = wpinv_is_test_mode( 'authorizenet' ) ? 'test' : 'live';
708
        }
709
710
        $url = $mode == 'test' ? 'https://sandbox.authorize.net/' : 'https://authorize.net/';
711
        $url .= 'ui/themes/sandbox/ARB/SubscriptionDetail.aspx?SubscrID=' . $profile_id;
712
713
        $link = '<a href="' . esc_url( $url ) . '" target="_blank">' . $profile_id . '</a>';
714
    }
715
    
716
    return apply_filters( 'wpinv_authorizenet_profile_id_link', $link, $profile_id, $subscription );
717
}
718
add_filter( 'wpinv_subscription_profile_link_authorizenet', 'wpinv_authorizenet_profile_id_link', 10, 2 );