1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* PayPal Standard Gateway |
4
|
|
|
* |
5
|
|
|
* @package Give |
6
|
|
|
* @subpackage Gateways |
7
|
|
|
* @copyright Copyright (c) 2016, WordImpress |
8
|
|
|
* @license https://opensource.org/licenses/gpl-license GNU Public License |
9
|
|
|
* @since 1.0 |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
13
|
|
|
exit; |
14
|
|
|
} |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Toggle PayPal CC Billing Detail Fieldset. |
18
|
|
|
* |
19
|
|
|
* @since 1.8.5 |
20
|
|
|
* |
21
|
|
|
* @param $form_id |
22
|
|
|
* |
23
|
|
|
* @return bool |
24
|
|
|
*/ |
25
|
|
|
function give_paypal_standard_billing_fields( $form_id ) { |
26
|
|
|
|
27
|
|
|
if ( give_is_setting_enabled( give_get_option( 'paypal_standard_billing_details' ) ) ) { |
28
|
|
|
give_default_cc_address_fields( $form_id ); |
29
|
|
|
|
30
|
|
|
return true; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
return false; |
34
|
|
|
|
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
add_action( 'give_paypal_cc_form', 'give_paypal_standard_billing_fields' ); |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Process PayPal Payment. |
41
|
|
|
* |
42
|
|
|
* @since 1.0 |
43
|
|
|
* |
44
|
|
|
* @param array $payment_data Payment data. |
45
|
|
|
* |
46
|
|
|
* @return void |
47
|
|
|
*/ |
48
|
|
|
function give_process_paypal_payment( $payment_data ) { |
49
|
|
|
|
50
|
|
|
// Validate nonce. |
51
|
|
|
give_validate_nonce( $payment_data['gateway_nonce'], 'give-gateway' ); |
52
|
|
|
$payment_id = give_create_payment( $payment_data ); |
53
|
|
|
|
54
|
|
|
// Check payment. |
55
|
|
|
if ( empty( $payment_id ) ) { |
56
|
|
|
// Record the error. |
57
|
|
|
give_record_gateway_error( __( 'Payment Error', 'give' ), sprintf( /* translators: %s: payment data */ |
58
|
|
|
__( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ), json_encode( $payment_data ) ), $payment_id ); |
59
|
|
|
// Problems? Send back. |
60
|
|
|
give_send_back_to_checkout( '?payment-mode=' . $payment_data['post_data']['give-gateway'] ); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
// Redirect to PayPal. |
64
|
|
|
wp_redirect( give_build_paypal_url( $payment_id, $payment_data ) ); |
65
|
|
|
exit; |
|
|
|
|
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
add_action( 'give_gateway_paypal', 'give_process_paypal_payment' ); |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Listens for a PayPal IPN requests and then sends to the processing function. |
72
|
|
|
* |
73
|
|
|
* @since 1.0 |
74
|
|
|
* @return void |
75
|
|
|
*/ |
76
|
|
|
function give_listen_for_paypal_ipn() { |
77
|
|
|
// Regular PayPal IPN |
78
|
|
|
if ( isset( $_GET['give-listener'] ) && $_GET['give-listener'] == 'IPN' ) { |
79
|
|
|
/** |
80
|
|
|
* Fires while verifying PayPal IPN |
81
|
|
|
* |
82
|
|
|
* @since 1.0 |
83
|
|
|
*/ |
84
|
|
|
do_action( 'give_verify_paypal_ipn' ); |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
add_action( 'init', 'give_listen_for_paypal_ipn' ); |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Process PayPal IPN |
92
|
|
|
* |
93
|
|
|
* @since 1.0 |
94
|
|
|
* @return void |
95
|
|
|
*/ |
96
|
|
|
function give_process_paypal_ipn() { |
97
|
|
|
|
98
|
|
|
// Check the request method is POST. |
99
|
|
|
if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== $_SERVER['REQUEST_METHOD'] ) { |
100
|
|
|
return; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
// Set initial post data to empty string. |
104
|
|
|
$post_data = ''; |
105
|
|
|
|
106
|
|
|
// Fallback just in case post_max_size is lower than needed. |
107
|
|
|
if ( ini_get( 'allow_url_fopen' ) ) { |
108
|
|
|
$post_data = file_get_contents( 'php://input' ); |
109
|
|
|
} else { |
110
|
|
|
// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough. |
111
|
|
|
ini_set( 'post_max_size', '12M' ); |
112
|
|
|
} |
113
|
|
|
// Start the encoded data collection with notification command. |
114
|
|
|
$encoded_data = 'cmd=_notify-validate'; |
115
|
|
|
|
116
|
|
|
// Get current arg separator. |
117
|
|
|
$arg_separator = give_get_php_arg_separator_output(); |
118
|
|
|
|
119
|
|
|
// Verify there is a post_data. |
120
|
|
|
if ( $post_data || strlen( $post_data ) > 0 ) { |
121
|
|
|
// Append the data. |
122
|
|
|
$encoded_data .= $arg_separator . $post_data; |
123
|
|
|
} else { |
124
|
|
|
// Check if POST is empty. |
125
|
|
|
if ( empty( $_POST ) ) { |
126
|
|
|
// Nothing to do. |
127
|
|
|
return; |
128
|
|
|
} else { |
129
|
|
|
// Loop through each POST. |
130
|
|
|
foreach ( $_POST as $key => $value ) { |
131
|
|
|
// Encode the value and append the data. |
132
|
|
|
$encoded_data .= $arg_separator . "$key=" . urlencode( $value ); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// Convert collected post data to an array. |
138
|
|
|
parse_str( $encoded_data, $encoded_data_array ); |
139
|
|
|
|
140
|
|
|
foreach ( $encoded_data_array as $key => $value ) { |
|
|
|
|
141
|
|
|
|
142
|
|
|
if ( false !== strpos( $key, 'amp;' ) ) { |
143
|
|
|
$new_key = str_replace( '&', '&', $key ); |
144
|
|
|
$new_key = str_replace( 'amp;', '&', $new_key ); |
145
|
|
|
|
146
|
|
|
unset( $encoded_data_array[ $key ] ); |
147
|
|
|
$encoded_data_array[ $new_key ] = $value; |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
// Validate IPN request w/ PayPal if user hasn't disabled this security measure. |
152
|
|
|
if ( give_is_setting_enabled( give_get_option( 'paypal_verification' ) ) ) { |
153
|
|
|
|
154
|
|
|
$remote_post_vars = array( |
155
|
|
|
'method' => 'POST', |
156
|
|
|
'timeout' => 45, |
157
|
|
|
'redirection' => 5, |
158
|
|
|
'httpversion' => '1.1', |
159
|
|
|
'blocking' => true, |
160
|
|
|
'headers' => array( |
161
|
|
|
'host' => 'www.paypal.com', |
162
|
|
|
'connection' => 'close', |
163
|
|
|
'content-type' => 'application/x-www-form-urlencoded', |
164
|
|
|
'post' => '/cgi-bin/webscr HTTP/1.1', |
165
|
|
|
|
166
|
|
|
), |
167
|
|
|
'sslverify' => false, |
168
|
|
|
'body' => $encoded_data_array, |
169
|
|
|
); |
170
|
|
|
|
171
|
|
|
// Validate the IPN. |
172
|
|
|
$api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars ); |
173
|
|
|
|
174
|
|
|
if ( is_wp_error( $api_response ) ) { |
175
|
|
|
give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ |
176
|
|
|
__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) ); |
177
|
|
|
|
178
|
|
|
return; // Something went wrong |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
if ( 'VERIFIED' !== $api_response['body'] ) { |
182
|
|
|
give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ |
183
|
|
|
__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) ); |
184
|
|
|
|
185
|
|
|
return; // Response not okay. |
186
|
|
|
} |
187
|
|
|
}// End if(). |
|
|
|
|
188
|
|
|
|
189
|
|
|
// Check if $post_data_array has been populated. |
190
|
|
|
if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) { |
191
|
|
|
return; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
$defaults = array( |
195
|
|
|
'txn_type' => '', |
196
|
|
|
'payment_status' => '', |
197
|
|
|
); |
198
|
|
|
|
199
|
|
|
$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults ); |
200
|
|
|
|
201
|
|
|
$payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0; |
202
|
|
|
$txn_type = $encoded_data_array['txn_type']; |
203
|
|
|
|
204
|
|
|
// Check for PayPal IPN Notifications and update data based on it. |
205
|
|
|
$current_timestamp = current_time( 'timestamp' ); |
206
|
|
|
$paypal_ipn_vars = array( |
207
|
|
|
'auth_status' => ( $api_response['body'] ) ? $api_response['body'] : 'N/A', |
|
|
|
|
208
|
|
|
'transaction_id' => $encoded_data_array['txn_id'], |
209
|
|
|
'payment_id' => $payment_id, |
210
|
|
|
); |
211
|
|
|
update_option( 'give_last_paypal_ipn_received', $paypal_ipn_vars ); |
212
|
|
|
give_insert_payment_note( $payment_id, sprintf( |
213
|
|
|
__( 'Last IPN received on %s at %s', 'give' ), |
214
|
|
|
date_i18n( 'm/d/Y', $current_timestamp ), |
215
|
|
|
date_i18n( 'H:i', $current_timestamp ) |
216
|
|
|
) |
217
|
|
|
); |
218
|
|
|
give_update_meta( $payment_id, 'give_last_paypal_ipn_received', $current_timestamp ); |
219
|
|
|
|
220
|
|
|
if ( has_action( 'give_paypal_' . $txn_type ) ) { |
221
|
|
|
/** |
222
|
|
|
* Fires while processing PayPal IPN $txn_type. |
223
|
|
|
* |
224
|
|
|
* Allow PayPal IPN types to be processed separately. |
225
|
|
|
* |
226
|
|
|
* @since 1.0 |
227
|
|
|
* |
228
|
|
|
* @param array $encoded_data_array Encoded data. |
229
|
|
|
* @param int $payment_id Payment id. |
230
|
|
|
*/ |
231
|
|
|
do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id ); |
232
|
|
|
} else { |
233
|
|
|
/** |
234
|
|
|
* Fires while process PayPal IPN. |
235
|
|
|
* |
236
|
|
|
* Fallback to web accept just in case the txn_type isn't present. |
237
|
|
|
* |
238
|
|
|
* @since 1.0 |
239
|
|
|
* |
240
|
|
|
* @param array $encoded_data_array Encoded data. |
241
|
|
|
* @param int $payment_id Payment id. |
242
|
|
|
*/ |
243
|
|
|
do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id ); |
244
|
|
|
} |
245
|
|
|
exit; |
|
|
|
|
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' ); |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Process web accept (one time) payment IPNs. |
252
|
|
|
* |
253
|
|
|
* @since 1.0 |
254
|
|
|
* |
255
|
|
|
* @param array $data The IPN Data. |
256
|
|
|
* @param int $payment_id The payment ID from Give. |
257
|
|
|
* |
258
|
|
|
* @return void |
259
|
|
|
*/ |
260
|
|
|
function give_process_paypal_web_accept( $data, $payment_id ) { |
261
|
|
|
|
262
|
|
|
// Only allow through these transaction types. |
263
|
|
|
if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) { |
264
|
|
|
return; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
// Need $payment_id to continue. |
268
|
|
|
if ( empty( $payment_id ) ) { |
269
|
|
|
return; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
// Collect donation payment details. |
273
|
|
|
$paypal_amount = $data['mc_gross']; |
274
|
|
|
$payment_status = strtolower( $data['payment_status'] ); |
275
|
|
|
$currency_code = strtolower( $data['mc_currency'] ); |
276
|
|
|
$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] ); |
277
|
|
|
$payment_meta = give_get_payment_meta( $payment_id ); |
278
|
|
|
|
279
|
|
|
// Must be a PayPal standard IPN. |
280
|
|
|
if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) { |
281
|
|
|
return; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
// Verify payment recipient |
285
|
|
|
if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) { |
286
|
|
|
|
287
|
|
|
give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ |
288
|
|
|
__( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id ); |
289
|
|
|
give_update_payment_status( $payment_id, 'failed' ); |
290
|
|
|
give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) ); |
291
|
|
|
|
292
|
|
|
return; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
// Verify payment currency. |
296
|
|
|
if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) { |
297
|
|
|
|
298
|
|
|
give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ |
299
|
|
|
__( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id ); |
300
|
|
|
give_update_payment_status( $payment_id, 'failed' ); |
301
|
|
|
give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) ); |
302
|
|
|
|
303
|
|
|
return; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
// Process refunds & reversed. |
307
|
|
|
if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) { |
308
|
|
|
give_process_paypal_refund( $data, $payment_id ); |
309
|
|
|
|
310
|
|
|
return; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
// Only complete payments once. |
314
|
|
|
if ( 'publish' === get_post_status( $payment_id ) ) { |
315
|
|
|
return; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
// Retrieve the total donation amount (before PayPal). |
319
|
|
|
$payment_amount = give_get_payment_amount( $payment_id ); |
320
|
|
|
|
321
|
|
|
// Check that the donation PP and local db amounts match. |
322
|
|
|
if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) { |
323
|
|
|
// The prices don't match |
324
|
|
|
give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ |
325
|
|
|
__( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id ); |
326
|
|
|
give_update_payment_status( $payment_id, 'failed' ); |
327
|
|
|
give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) ); |
328
|
|
|
|
329
|
|
|
return; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
// Process completed donations. |
333
|
|
|
if ( 'completed' === $payment_status || give_is_test_mode() ) { |
334
|
|
|
|
335
|
|
|
give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */ |
336
|
|
|
__( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id'] ) ); |
337
|
|
|
give_set_payment_transaction_id( $payment_id, $data['txn_id'] ); |
338
|
|
|
give_update_payment_status( $payment_id, 'publish' ); |
339
|
|
|
|
340
|
|
|
} elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) { |
341
|
|
|
|
342
|
|
|
// Look for possible pending reasons, such as an echeck. |
343
|
|
|
$note = give_paypal_get_pending_donation_note( strtolower( $data['pending_reason'] ) ); |
344
|
|
|
|
345
|
|
|
if ( ! empty( $note ) ) { |
346
|
|
|
give_insert_payment_note( $payment_id, $note ); |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 ); |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Process PayPal IPN Refunds |
356
|
|
|
* |
357
|
|
|
* @since 1.0 |
358
|
|
|
* |
359
|
|
|
* @param array $data IPN Data |
360
|
|
|
* @param int $payment_id The payment ID. |
361
|
|
|
* |
362
|
|
|
* @return void |
363
|
|
|
*/ |
364
|
|
|
function give_process_paypal_refund( $data, $payment_id = 0 ) { |
365
|
|
|
|
366
|
|
|
// Collect payment details. |
367
|
|
|
if ( empty( $payment_id ) ) { |
368
|
|
|
return; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
// Only refund payments once. |
372
|
|
|
if ( 'refunded' === get_post_status( $payment_id ) ) { |
373
|
|
|
return; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
$payment_amount = give_get_payment_amount( $payment_id ); |
377
|
|
|
$refund_amount = $data['payment_gross'] * - 1; |
378
|
|
|
|
379
|
|
|
if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) { |
380
|
|
|
|
381
|
|
|
give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal parent transaction ID */ |
382
|
|
|
__( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id'] ) ); |
383
|
|
|
|
384
|
|
|
return; // This is a partial refund |
385
|
|
|
|
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
give_insert_payment_note( $payment_id, sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */ |
389
|
|
|
__( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ), $data['parent_txn_id'], $data['reason_code'] ) ); |
390
|
|
|
give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */ |
391
|
|
|
__( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id'] ) ); |
392
|
|
|
give_update_payment_status( $payment_id, 'refunded' ); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Get PayPal Redirect |
397
|
|
|
* |
398
|
|
|
* @since 1.0 |
399
|
|
|
* |
400
|
|
|
* @param bool $ssl_check Is SSL? |
401
|
|
|
* |
402
|
|
|
* @return string |
403
|
|
|
*/ |
404
|
|
|
function give_get_paypal_redirect( $ssl_check = false ) { |
405
|
|
|
|
406
|
|
|
if ( is_ssl() || ! $ssl_check ) { |
407
|
|
|
$protocol = 'https://'; |
408
|
|
|
} else { |
409
|
|
|
$protocol = 'http://'; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
// Check the current payment mode |
413
|
|
|
if ( give_is_test_mode() ) { |
414
|
|
|
// Test mode |
415
|
|
|
$paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr'; |
416
|
|
|
} else { |
417
|
|
|
// Live mode |
418
|
|
|
$paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr'; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
return apply_filters( 'give_paypal_uri', $paypal_uri ); |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* Set the Page Style for offsite PayPal page. |
426
|
|
|
* |
427
|
|
|
* @since 1.0 |
428
|
|
|
* @return string |
429
|
|
|
*/ |
430
|
|
|
function give_get_paypal_page_style() { |
431
|
|
|
$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) ); |
432
|
|
|
|
433
|
|
|
return apply_filters( 'give_paypal_page_style', $page_style ); |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* PayPal Success Page |
438
|
|
|
* |
439
|
|
|
* Shows "Donation Processing" message for PayPal payments that are still pending on site return |
440
|
|
|
* |
441
|
|
|
* @since 1.0 |
442
|
|
|
* |
443
|
|
|
* @param $content |
444
|
|
|
* |
445
|
|
|
* @return string |
446
|
|
|
*/ |
447
|
|
|
function give_paypal_success_page_content( $content ) { |
448
|
|
|
|
449
|
|
|
if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) { |
450
|
|
|
return $content; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false; |
454
|
|
|
|
455
|
|
|
if ( ! $payment_id ) { |
456
|
|
|
$session = give_get_purchase_session(); |
457
|
|
|
$payment_id = give_get_purchase_id_by_key( $session['purchase_key'] ); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
$payment = get_post( $payment_id ); |
461
|
|
|
if ( $payment && 'pending' === $payment->post_status ) { |
462
|
|
|
|
463
|
|
|
// Payment is still pending so show processing indicator to fix the race condition. |
464
|
|
|
ob_start(); |
465
|
|
|
|
466
|
|
|
give_get_template_part( 'payment', 'processing' ); |
467
|
|
|
|
468
|
|
|
$content = ob_get_clean(); |
469
|
|
|
|
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
return $content; |
473
|
|
|
|
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' ); |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Given a Payment ID, extract the transaction ID |
480
|
|
|
* |
481
|
|
|
* @since 1.0 |
482
|
|
|
* |
483
|
|
|
* @param string $payment_id Payment ID |
484
|
|
|
* |
485
|
|
|
* @return string Transaction ID |
486
|
|
|
*/ |
487
|
|
|
function give_paypal_get_payment_transaction_id( $payment_id ) { |
488
|
|
|
|
489
|
|
|
$transaction_id = ''; |
490
|
|
|
$notes = give_get_payment_notes( $payment_id ); |
491
|
|
|
|
492
|
|
|
foreach ( $notes as $note ) { |
493
|
|
|
if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note->comment_content, $match ) ) { |
494
|
|
|
$transaction_id = $match[1]; |
495
|
|
|
continue; |
496
|
|
|
} |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
return apply_filters( 'give_paypal_set_payment_transaction_id', $transaction_id, $payment_id ); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
add_filter( 'give_get_payment_transaction_id-paypal', 'give_paypal_get_payment_transaction_id', 10, 1 ); |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* Given a transaction ID, generate a link to the PayPal transaction ID details |
506
|
|
|
* |
507
|
|
|
* @since 1.0 |
508
|
|
|
* |
509
|
|
|
* @param string $transaction_id The Transaction ID |
510
|
|
|
* @param int $payment_id The payment ID for this transaction |
511
|
|
|
* |
512
|
|
|
* @return string A link to the PayPal transaction details |
513
|
|
|
*/ |
514
|
|
|
function give_paypal_link_transaction_id( $transaction_id, $payment_id ) { |
|
|
|
|
515
|
|
|
|
516
|
|
|
$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id='; |
517
|
|
|
$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>'; |
518
|
|
|
|
519
|
|
|
return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url ); |
520
|
|
|
|
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 ); |
524
|
|
|
|
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* Get pending donation note. |
528
|
|
|
* |
529
|
|
|
* @since 1.6.3 |
530
|
|
|
* |
531
|
|
|
* @param $pending_reason |
532
|
|
|
* |
533
|
|
|
* @return string |
534
|
|
|
*/ |
535
|
|
|
function give_paypal_get_pending_donation_note( $pending_reason ) { |
536
|
|
|
|
537
|
|
|
$note = ''; |
538
|
|
|
|
539
|
|
|
switch ( $pending_reason ) { |
540
|
|
|
|
541
|
|
|
case 'echeck' : |
542
|
|
|
|
543
|
|
|
$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' ); |
544
|
|
|
|
545
|
|
|
break; |
546
|
|
|
|
547
|
|
|
case 'address' : |
548
|
|
|
|
549
|
|
|
$note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' ); |
550
|
|
|
|
551
|
|
|
break; |
552
|
|
|
|
553
|
|
|
case 'intl' : |
554
|
|
|
|
555
|
|
|
$note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' ); |
556
|
|
|
|
557
|
|
|
break; |
558
|
|
|
|
559
|
|
|
case 'multi-currency' : |
560
|
|
|
|
561
|
|
|
$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' ); |
562
|
|
|
|
563
|
|
|
break; |
564
|
|
|
|
565
|
|
|
case 'paymentreview' : |
566
|
|
|
case 'regulatory_review' : |
567
|
|
|
|
568
|
|
|
$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' ); |
569
|
|
|
|
570
|
|
|
break; |
571
|
|
|
|
572
|
|
|
case 'unilateral' : |
573
|
|
|
|
574
|
|
|
$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' ); |
575
|
|
|
|
576
|
|
|
break; |
577
|
|
|
|
578
|
|
|
case 'upgrade' : |
579
|
|
|
|
580
|
|
|
$note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' ); |
581
|
|
|
|
582
|
|
|
break; |
583
|
|
|
|
584
|
|
|
case 'verify' : |
585
|
|
|
|
586
|
|
|
$note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' ); |
587
|
|
|
|
588
|
|
|
break; |
589
|
|
|
|
590
|
|
|
case 'other' : |
591
|
|
|
|
592
|
|
|
$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' ); |
593
|
|
|
|
594
|
|
|
break; |
595
|
|
|
|
596
|
|
|
}// End switch(). |
|
|
|
|
597
|
|
|
|
598
|
|
|
return $note; |
599
|
|
|
|
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* Build paypal url |
604
|
|
|
* |
605
|
|
|
* @param int $payment_id Payment ID |
606
|
|
|
* @param array $payment_data Array of payment data. |
607
|
|
|
* |
608
|
|
|
* @return mixed|string |
609
|
|
|
*/ |
610
|
|
|
function give_build_paypal_url( $payment_id, $payment_data ) { |
611
|
|
|
// Only send to PayPal if the pending payment is created successfully. |
612
|
|
|
$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) ); |
613
|
|
|
|
614
|
|
|
// Get the success url. |
615
|
|
|
$return_url = add_query_arg( array( |
616
|
|
|
'payment-confirmation' => 'paypal', |
617
|
|
|
'payment-id' => $payment_id, |
618
|
|
|
|
619
|
|
|
), get_permalink( give_get_option( 'success_page' ) ) ); |
620
|
|
|
|
621
|
|
|
// Get the PayPal redirect uri. |
622
|
|
|
$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?'; |
623
|
|
|
|
624
|
|
|
// Item name. |
625
|
|
|
$item_name = give_build_paypal_item_title( $payment_data ); |
626
|
|
|
|
627
|
|
|
// Setup PayPal API params. |
628
|
|
|
$paypal_args = array( |
629
|
|
|
'business' => give_get_option( 'paypal_email', false ), |
630
|
|
|
'first_name' => $payment_data['user_info']['first_name'], |
631
|
|
|
'last_name' => $payment_data['user_info']['last_name'], |
632
|
|
|
'email' => $payment_data['user_email'], |
633
|
|
|
'invoice' => $payment_data['purchase_key'], |
634
|
|
|
'amount' => $payment_data['price'], |
635
|
|
|
'item_name' => stripslashes( $item_name ), |
636
|
|
|
'no_shipping' => '1', |
637
|
|
|
'shipping' => '0', |
638
|
|
|
'no_note' => '1', |
639
|
|
|
'currency_code' => give_get_currency(), |
640
|
|
|
'charset' => get_bloginfo( 'charset' ), |
641
|
|
|
'custom' => $payment_id, |
642
|
|
|
'rm' => '2', |
643
|
|
|
'return' => $return_url, |
644
|
|
|
'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ), |
645
|
|
|
'notify_url' => $listener_url, |
646
|
|
|
'page_style' => give_get_paypal_page_style(), |
647
|
|
|
'cbt' => get_bloginfo( 'name' ), |
648
|
|
|
'bn' => 'givewp_SP', |
649
|
|
|
); |
650
|
|
|
|
651
|
|
|
// Add user address if present. |
652
|
|
|
if ( ! empty( $payment_data['user_info']['address'] ) ) { |
653
|
|
|
$default_address = array( |
654
|
|
|
'line1' => '', |
655
|
|
|
'line2' => '', |
656
|
|
|
'city' => '', |
657
|
|
|
'state' => '', |
658
|
|
|
'zip' => '', |
659
|
|
|
'country' => '', |
660
|
|
|
); |
661
|
|
|
|
662
|
|
|
$address = wp_parse_args( $payment_data['user_info']['address'], $default_address ); |
663
|
|
|
|
664
|
|
|
$paypal_args['address1'] = $address['line1']; |
665
|
|
|
$paypal_args['address2'] = $address['line2']; |
666
|
|
|
$paypal_args['city'] = $address['city']; |
667
|
|
|
$paypal_args['state'] = $address['state']; |
668
|
|
|
$paypal_args['zip'] = $address['zip']; |
669
|
|
|
$paypal_args['country'] = $address['country']; |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
// Donations or regular transactions? |
673
|
|
|
$paypal_args['cmd'] = give_get_paypal_button_type(); |
674
|
|
|
|
675
|
|
|
/** |
676
|
|
|
* Filter the paypal redirect args. |
677
|
|
|
* |
678
|
|
|
* @since 1.8 |
679
|
|
|
* |
680
|
|
|
* @param array $paypal_args |
681
|
|
|
* @param array $payment_data |
682
|
|
|
*/ |
683
|
|
|
$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data ); |
684
|
|
|
|
685
|
|
|
// Build query. |
686
|
|
|
$paypal_redirect .= http_build_query( $paypal_args ); |
687
|
|
|
|
688
|
|
|
// Fix for some sites that encode the entities. |
689
|
|
|
$paypal_redirect = str_replace( '&', '&', $paypal_redirect ); |
690
|
|
|
|
691
|
|
|
return $paypal_redirect; |
692
|
|
|
} |
693
|
|
|
|
694
|
|
|
|
695
|
|
|
/** |
696
|
|
|
* Get paypal button type. |
697
|
|
|
* |
698
|
|
|
* @since 1.8 |
699
|
|
|
* @return string |
700
|
|
|
*/ |
701
|
|
|
function give_get_paypal_button_type() { |
702
|
|
|
// paypal_button_type can be donation or standard. |
703
|
|
|
$paypal_button_type = '_donations'; |
704
|
|
|
if ( 'standard' === give_get_option( 'paypal_button_type' ) ) { |
705
|
|
|
$paypal_button_type = '_xclick'; |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
return $paypal_button_type; |
709
|
|
|
} |
710
|
|
|
|
711
|
|
|
|
712
|
|
|
/** |
713
|
|
|
* Build item title for paypal. |
714
|
|
|
* |
715
|
|
|
* @since 1.8 |
716
|
|
|
* |
717
|
|
|
* @param $payment_data |
718
|
|
|
* |
719
|
|
|
* @return string |
720
|
|
|
*/ |
721
|
|
|
function give_build_paypal_item_title( $payment_data ) { |
722
|
|
|
$form_id = intval( $payment_data['post_data']['give-form-id'] ); |
723
|
|
|
$item_name = $payment_data['post_data']['give-form-title']; |
724
|
|
|
|
725
|
|
|
// Verify has variable prices. |
726
|
|
|
if ( give_has_variable_prices( $form_id ) && isset( $payment_data['post_data']['give-price-id'] ) ) { |
727
|
|
|
|
728
|
|
|
$item_price_level_text = give_get_price_option_name( $form_id, $payment_data['post_data']['give-price-id'] ); |
729
|
|
|
$price_level_amount = give_get_price_option_amount( $form_id, $payment_data['post_data']['give-price-id'] ); |
730
|
|
|
|
731
|
|
|
// Donation given doesn't match selected level (must be a custom amount). |
732
|
|
|
if ( $price_level_amount != give_sanitize_amount( $payment_data['price'] ) ) { |
733
|
|
|
$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true ); |
734
|
|
|
// user custom amount text if any, fallback to default if not. |
735
|
|
|
$item_name .= ' - ' . give_check_variable( $custom_amount_text, 'empty', __( 'Custom Amount', 'give' ) ); |
736
|
|
|
|
737
|
|
|
} elseif ( ! empty( $item_price_level_text ) ) { |
738
|
|
|
$item_name .= ' - ' . $item_price_level_text; |
739
|
|
|
} |
740
|
|
|
} // End if(). |
|
|
|
|
741
|
|
|
elseif ( give_get_form_price( $form_id ) !== give_sanitize_amount( $payment_data['price'] ) ) { |
742
|
|
|
$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true ); |
743
|
|
|
// user custom amount text if any, fallback to default if not. |
744
|
|
|
$item_name .= ' - ' . give_check_variable( $custom_amount_text, 'empty', __( 'Custom Amount', 'give' ) ); |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
return $item_name; |
748
|
|
|
} |
749
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.