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