PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.7.4
GiveWP – Donation Plugin and Fundraising Platform v2.7.4
4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / includes / gateways / paypal-standard.php
give / includes / gateways Last commit date
stripe 6 years ago actions.php 6 years ago functions.php 6 years ago manual.php 6 years ago offline-donations.php 6 years ago paypal-standard.php 6 years ago
paypal-standard.php
789 lines
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 use Give\Helpers\Form\Utils as FormUtils;
17
18 /**
19 * Toggle PayPal CC Billing Detail Fieldset.
20 *
21 * @param int $form_id Form ID.
22 *
23 * @return bool
24 * @since 1.8.5
25 */
26 function give_paypal_standard_billing_fields( $form_id ) {
27
28 if ( give_is_setting_enabled( give_get_option( 'paypal_standard_billing_details' ) ) ) {
29 give_default_cc_address_fields( $form_id );
30
31 return true;
32 }
33
34 if ( FormUtils::isLegacyForm( $form_id ) ) {
35 return false;
36 }
37
38 printf(
39 '
40 <fieldset class="no-fields">
41 <div style="display: flex; justify-content: center; margin-top: 20px;">
42 <svg width="250" height="66" viewBox="0 0 250 66" fill="none" xmlns="http://www.w3.org/2000/svg">
43 <path fill-rule="evenodd" clip-rule="evenodd" d="M247.001 0H239.683C239.679 0 239.675 0.00133897 239.672 0.00133897L239.668 0C237.993 0 236.294 1.24922 235.819 2.83853C235.8 2.9028 235.767 2.96439 235.752 3.03134C235.752 3.03134 235.577 3.80658 235.275 5.14685L225.223 49.5056C224.991 50.5085 224.836 51.1618 224.789 51.3319L224.808 51.356C224.46 52.9065 225.417 54.1892 226.989 54.3324L227.01 54.3619H234.605C236.269 54.3619 237.958 53.1247 238.447 51.5555C238.471 51.4792 238.508 51.4082 238.526 51.3319L249.49 3.02866L249.473 3.0233C249.846 1.37106 248.753 0 247.001 0ZM209.468 43.5965C208.453 44.2191 207.38 44.752 206.257 45.2059C204.747 45.799 203.315 46.1096 201.991 46.1096C199.958 46.1096 198.396 45.8298 197.34 45.2313C196.283 44.6636 195.728 43.6768 195.746 42.2642C195.746 40.6334 196.133 39.3681 196.933 38.376C197.739 37.4213 198.937 36.6528 200.422 36.0797C201.899 35.5923 203.716 35.1907 205.813 34.9028C207.677 34.6738 211.364 34.2601 211.836 34.2574C212.308 34.2534 212.621 34.0004 212.411 35.2241C212.317 35.7557 211.253 40.0108 210.765 41.9456C210.623 42.5253 209.831 43.3702 209.468 43.5965C209.831 43.3702 209.468 43.5965 209.468 43.5965ZM223.935 13.6902C220.83 12.0031 215.973 11.1502 209.326 11.1502C206.04 11.1502 202.737 11.4073 199.422 11.908C196.99 12.2709 196.739 12.3311 195.228 12.6511C192.119 13.3099 191.639 16.3412 191.639 16.3412L190.64 20.3393C190.074 22.8966 191.57 22.7909 192.237 22.5927C193.594 22.1937 194.33 21.7974 197.1 21.1868C199.747 20.6017 202.544 20.1639 204.776 20.1813C208.049 20.1813 210.538 20.5307 212.192 21.2029C213.848 21.9058 214.668 23.1028 214.668 24.8153C214.673 25.2224 214.684 25.6066 214.533 25.9427C214.397 26.256 214.136 26.5586 213.353 26.6617C208.682 26.9322 205.329 27.3472 201.291 27.9203C197.307 28.4666 193.821 29.4025 190.907 30.6986C187.801 32.0402 185.477 33.8357 183.874 36.1105C182.315 38.3961 181.531 41.1637 181.527 44.424C181.527 47.5048 182.664 50.018 184.865 51.9688C187.091 53.8942 189.987 54.8475 193.491 54.8475C195.68 54.8328 197.394 54.6788 198.622 54.3802C199.838 54.0817 201.163 53.6572 202.563 53.0667C203.61 52.641 204.739 52.0251 205.931 51.2525C207.125 50.4772 207.951 49.9269 209.012 49.2508L209.05 49.3151L208.752 50.5964C208.75 50.6085 208.732 50.6152 208.732 50.6272L208.742 50.6513C208.401 52.1951 209.354 53.4791 210.924 53.6304L210.944 53.6572H211.083L211.088 53.6639C212.131 53.6639 215.709 53.6626 217.381 53.6572H218.549C218.626 53.6572 218.634 53.6344 218.664 53.617C220.268 53.4256 221.805 52.1536 222.167 50.6272L228.139 25.529C228.277 24.9385 228.385 24.257 228.45 23.4724C228.524 22.6797 228.612 22.0277 228.586 21.5564C228.6 18.0042 227.032 15.3785 223.935 13.6902ZM187.973 7.97333C186.954 6.04259 185.415 4.48809 183.469 3.2777C181.474 2.06999 179.103 1.2278 176.358 0.736411C173.643 0.273142 170.416 0.00803362 166.738 0L149.628 0.00803362C147.867 0.0374902 146.138 1.38445 145.731 3.04339L134.228 51.7831C133.808 53.4394 134.952 54.8198 136.682 54.8104L144.892 54.7957C146.633 54.8104 148.407 53.4394 148.818 51.7831L151.593 39.955C151.983 38.2974 153.753 36.9089 155.522 36.933H157.856C167.87 36.933 175.635 34.93 181.194 30.9427C186.741 26.9353 189.532 21.6786 189.532 15.1406C189.514 12.286 189.015 9.8813 187.973 7.97333ZM170.319 23.9146C167.868 25.6499 164.429 26.5202 160.004 26.5202H157.968C156.208 26.5403 155.071 25.1545 155.489 23.4929L157.942 13.1483C158.308 11.5121 160.094 10.1197 161.83 10.133L164.518 10.1197C167.657 10.133 170.004 10.6445 171.625 11.6608C173.219 12.6891 173.992 14.2837 174.004 16.4086C174.011 19.6475 172.78 22.1392 170.319 23.9146Z" fill="#306FC5"/>
44 <path fill-rule="evenodd" clip-rule="evenodd" d="M131.11 11.1504C129.552 11.1504 127.509 12.3854 126.541 13.8779C126.541 13.8779 116.12 31.3785 115.096 33.1273C114.54 34.0676 113.976 33.4699 113.882 33.1202C113.806 32.6949 110.652 13.9929 110.652 13.9929C110.298 12.4872 108.692 11.1947 106.684 11.2007L100.185 11.2103C98.6189 11.2103 97.6418 12.4393 97.9903 13.9246C97.9903 13.9246 102.958 41.5039 103.926 47.9902C104.409 51.5778 103.876 52.2138 103.876 52.2138L97.4364 63.2125C96.4935 64.7038 97.0096 65.9256 98.5688 65.9256L106.099 65.9196C107.659 65.9196 109.724 64.7038 110.652 63.2101L139.623 15.2159C139.623 15.2159 142.394 11.1157 139.838 11.1504C138.095 11.1744 131.11 11.1504 131.11 11.1504ZM76.7204 43.5965C75.7055 44.2191 74.6342 44.7493 73.5107 45.2046C72.0007 45.795 70.5623 46.1083 69.2421 46.1083C67.215 46.1083 65.6487 45.8272 64.5912 45.2313C63.535 44.6623 62.9781 43.6755 62.9959 42.2616C62.9959 40.6348 63.3865 39.3655 64.1855 38.3747C64.9927 37.4187 66.185 36.6488 67.6702 36.0771C69.1513 35.587 70.9734 35.1907 73.0637 34.9028C74.9285 34.6739 78.6195 34.2615 79.0898 34.2548C79.5574 34.2535 79.8723 33.9977 79.6619 35.2215C79.5711 35.7531 78.5054 40.0109 78.0172 41.9443C77.87 42.5267 77.0779 43.3716 76.7204 43.5965C77.0779 43.3716 76.7204 43.5965 76.7204 43.5965ZM91.192 13.6888C88.0868 12.0018 83.231 11.1502 76.5847 11.1502C73.2966 11.1502 69.9948 11.4046 66.6792 11.9067C64.2424 12.2669 63.9976 12.3285 62.4849 12.6485C59.3742 13.3099 58.8956 16.3386 58.8956 16.3386L57.8972 20.3393C57.332 22.8953 58.8296 22.7895 59.4938 22.5914C60.8484 22.1924 61.5882 21.7974 64.3579 21.1841C67.001 20.6017 69.8023 20.1652 72.0314 20.1799C75.3072 20.1799 77.7935 20.5281 79.4479 21.2002C81.105 21.9031 81.9205 23.1028 81.9205 24.814C81.9315 25.221 81.9397 25.6066 81.7926 25.94C81.6537 26.256 81.3896 26.5573 80.6072 26.659C75.9411 26.9335 72.5898 27.3472 68.5453 27.9203C64.5614 28.4666 61.0767 29.4025 58.1613 30.6972C55.0547 32.0388 52.7334 33.837 51.134 36.1105C49.5691 38.3947 48.7866 41.1623 48.7838 44.424C48.7838 47.5035 49.9225 50.018 52.12 51.9675C54.3479 53.8929 57.2413 54.8475 60.7452 54.8475C62.9359 54.8328 64.648 54.6761 65.8802 54.3802C67.0945 54.079 68.4216 53.6572 69.816 53.0667C70.8667 52.641 71.9929 52.0224 73.1866 51.2512C74.3775 50.4773 75.2081 49.9269 76.2725 49.2508L76.3055 49.3137L76.0071 50.5937C76.0044 50.6058 75.9879 50.6138 75.9879 50.6286L76.0003 50.6486C75.6592 52.1964 76.6095 53.4791 78.1813 53.6278L78.2006 53.6572H78.3381L78.3422 53.6666C79.3874 53.6666 82.967 53.6612 84.6351 53.6572H85.804C85.8824 53.6572 85.8934 53.6318 85.9181 53.6157C87.5257 53.4189 89.0577 52.1496 89.4235 50.6286L95.396 25.5276C95.5307 24.9385 95.6408 24.257 95.7013 23.4737C95.781 22.677 95.8677 22.0277 95.8443 21.5577C95.858 18.0028 94.2876 15.3772 91.192 13.6888ZM53.8322 7.97333C52.8132 6.04259 51.2743 4.48809 49.3284 3.2777C47.333 2.06999 44.9622 1.2278 42.2173 0.736411C39.5027 0.273142 36.2751 0.00803362 32.5978 0L15.4877 0.00803362C13.7261 0.0374902 11.9974 1.38445 11.5904 3.04339L0.0869061 51.7831C-0.332528 53.4394 0.811633 54.8198 2.54163 54.8104L10.7515 54.7957C12.4925 54.8104 14.2665 53.4394 14.6777 51.7831L17.4528 39.955C17.842 38.2974 19.6119 36.9089 21.3818 36.933H23.7155C33.7296 36.933 41.494 34.93 47.0539 30.9427C52.6 26.9353 55.3917 21.6786 55.3917 15.1406C55.3738 12.286 54.8746 9.8813 53.8322 7.97333ZM36.1787 23.9146C33.7281 25.6499 30.2887 26.5202 25.8633 26.5202H23.8281C22.0678 26.5403 20.9305 25.1545 21.3486 23.4929L23.8019 13.1483C24.1677 11.5121 25.9541 10.1197 27.6896 10.133L30.3781 10.1197C33.5163 10.133 35.8637 10.6445 37.4851 11.6608C39.0789 12.6891 39.8518 14.2837 39.8642 16.4086C39.871 19.6475 38.6402 22.1392 36.1787 23.9146Z" fill="#265697"/>
45 </svg>
46 </div>
47 <p style="text-align: center;"><b>%1$s</b></p>
48 <p style="text-align: center;">
49 <b>%2$s</b> %3$s
50 </p>
51 </fieldset>
52 ',
53 __( 'Make your donation quickly and securely with PayPal', 'give' ),
54 __( 'How it works:', 'give' ),
55 __( 'You will be redirected to PayPal to pay using your PayPal account, or with a credit or debit card. You will then be brought back to this page to view your receipt.', 'give' )
56 );
57
58 return true;
59
60 }
61
62 add_action( 'give_paypal_cc_form', 'give_paypal_standard_billing_fields' );
63
64 /**
65 * Process PayPal Payment.
66 *
67 * @param array $payment_data Payment data.
68 *
69 * @return void
70 * @since 1.0
71 */
72 function give_process_paypal_payment( $payment_data ) {
73
74 // Validate nonce.
75 give_validate_nonce( $payment_data['gateway_nonce'], 'give-gateway' );
76
77 $payment_id = give_create_payment( $payment_data );
78
79 // Check payment.
80 if ( empty( $payment_id ) ) {
81 // Record the error.
82 give_record_gateway_error(
83 __( 'Payment Error', 'give' ),
84 sprintf( /* translators: %s: payment data */
85 __( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ),
86 json_encode( $payment_data )
87 ),
88 $payment_id
89 );
90 // Problems? Send back.
91 give_send_back_to_checkout( '?payment-mode=' . $payment_data['post_data']['give-gateway'] );
92 }
93
94 // Redirect to PayPal.
95 wp_redirect( give_build_paypal_url( $payment_id, $payment_data ) );
96 }
97
98 add_action( 'give_gateway_paypal', 'give_process_paypal_payment' );
99
100 /**
101 * Listens for a PayPal IPN requests and then sends to the processing function.
102 *
103 * @return void
104 * @since 1.0
105 */
106 function give_listen_for_paypal_ipn() {
107
108 // Regular PayPal IPN.
109 if ( isset( $_GET['give-listener'] ) && 'IPN' === $_GET['give-listener'] ) {
110 /**
111 * Fires while verifying PayPal IPN
112 *
113 * @since 1.0
114 */
115 do_action( 'give_verify_paypal_ipn' );
116 }
117 }
118
119 add_action( 'init', 'give_listen_for_paypal_ipn' );
120
121 /**
122 * Process PayPal IPN
123 *
124 * @return void
125 * @since 1.0
126 */
127 function give_process_paypal_ipn() {
128
129 // Check the request method is POST.
130 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
131 return;
132 }
133
134 // Set initial post data to empty string.
135 $post_data = '';
136
137 // Fallback just in case post_max_size is lower than needed.
138 if ( ini_get( 'allow_url_fopen' ) ) {
139 $post_data = file_get_contents( 'php://input' );
140 } else {
141 // If allow_url_fopen is not enabled, then make sure that post_max_size is large enough.
142 ini_set( 'post_max_size', '12M' );
143 }
144 // Start the encoded data collection with notification command.
145 $encoded_data = 'cmd=_notify-validate';
146
147 // Get current arg separator.
148 $arg_separator = give_get_php_arg_separator_output();
149
150 // Verify there is a post_data.
151 if ( $post_data || strlen( $post_data ) > 0 ) {
152 // Append the data.
153 $encoded_data .= $arg_separator . $post_data;
154 } else {
155 // Check if POST is empty.
156 if ( empty( $_POST ) ) {
157 // Nothing to do.
158 return;
159 } else {
160 // Loop through each POST.
161 foreach ( $_POST as $key => $value ) {
162 // Encode the value and append the data.
163 $encoded_data .= $arg_separator . "$key=" . urlencode( $value );
164 }
165 }
166 }
167
168 // Convert collected post data to an array.
169 parse_str( $encoded_data, $encoded_data_array );
170
171 foreach ( $encoded_data_array as $key => $value ) {
172
173 if ( false !== strpos( $key, 'amp;' ) ) {
174 $new_key = str_replace( '&amp;', '&', $key );
175 $new_key = str_replace( 'amp;', '&', $new_key );
176
177 unset( $encoded_data_array[ $key ] );
178 $encoded_data_array[ $new_key ] = $value;
179 }
180 }
181
182 $api_response = false;
183
184 // Validate IPN request w/ PayPal if user hasn't disabled this security measure.
185 if ( give_is_setting_enabled( give_get_option( 'paypal_verification' ) ) ) {
186
187 $remote_post_vars = [
188 'method' => 'POST',
189 'timeout' => 45,
190 'redirection' => 5,
191 'httpversion' => '1.1',
192 'blocking' => true,
193 'headers' => [
194 'host' => 'www.paypal.com',
195 'connection' => 'close',
196 'content-type' => 'application/x-www-form-urlencoded',
197 'post' => '/cgi-bin/webscr HTTP/1.1',
198
199 ],
200 'sslverify' => false,
201 'body' => $encoded_data_array,
202 ];
203
204 // Validate the IPN.
205 $api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars );
206
207 if ( is_wp_error( $api_response ) ) {
208 give_record_gateway_error(
209 __( 'IPN Error', 'give' ),
210 sprintf( /* translators: %s: Paypal IPN response */
211 __( 'Invalid IPN verification response. IPN data: %s', 'give' ),
212 json_encode( $api_response )
213 )
214 );
215
216 return; // Something went wrong.
217 }
218
219 if ( 'VERIFIED' !== $api_response['body'] ) {
220 give_record_gateway_error(
221 __( 'IPN Error', 'give' ),
222 sprintf( /* translators: %s: Paypal IPN response */
223 __( 'Invalid IPN verification response. IPN data: %s', 'give' ),
224 json_encode( $api_response )
225 )
226 );
227
228 return; // Response not okay.
229 }
230 }// End if().
231
232 // Check if $post_data_array has been populated.
233 if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) {
234 return;
235 }
236
237 $defaults = [
238 'txn_type' => '',
239 'payment_status' => '',
240 ];
241
242 $encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
243
244 $payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
245 $txn_type = $encoded_data_array['txn_type'];
246
247 // Check for PayPal IPN Notifications and update data based on it.
248 $current_timestamp = current_time( 'timestamp' );
249 $paypal_ipn_vars = [
250 'auth_status' => isset( $api_response['body'] ) ? $api_response['body'] : 'N/A',
251 'transaction_id' => isset( $encoded_data_array['txn_id'] ) ? $encoded_data_array['txn_id'] : 'N/A',
252 'payment_id' => $payment_id,
253 ];
254 update_option( 'give_last_paypal_ipn_received', $paypal_ipn_vars, false );
255 give_insert_payment_note(
256 $payment_id,
257 sprintf(
258 __( 'IPN received on %1$s at %2$s', 'give' ),
259 date_i18n( 'm/d/Y', $current_timestamp ),
260 date_i18n( 'H:i', $current_timestamp )
261 )
262 );
263 give_update_meta( $payment_id, 'give_last_paypal_ipn_received', $current_timestamp );
264
265 if ( has_action( 'give_paypal_' . $txn_type ) ) {
266 /**
267 * Fires while processing PayPal IPN $txn_type.
268 *
269 * Allow PayPal IPN types to be processed separately.
270 *
271 * @param array $encoded_data_array Encoded data.
272 * @param int $payment_id Payment id.
273 *
274 * @since 1.0
275 */
276 do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id );
277 } else {
278 /**
279 * Fires while process PayPal IPN.
280 *
281 * Fallback to web accept just in case the txn_type isn't present.
282 *
283 * @param array $encoded_data_array Encoded data.
284 * @param int $payment_id Payment id.
285 *
286 * @since 1.0
287 */
288 do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
289 }
290 exit;
291 }
292
293 add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
294
295 /**
296 * Process web accept (one time) payment IPNs.
297 *
298 * @param array $data The IPN Data.
299 * @param int $payment_id The payment ID from Give.
300 *
301 * @return void
302 * @since 1.0
303 */
304 function give_process_paypal_web_accept( $data, $payment_id ) {
305
306 // Only allow through these transaction types.
307 if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) {
308 return;
309 }
310
311 // Need $payment_id to continue.
312 if ( empty( $payment_id ) ) {
313 return;
314 }
315
316 // Collect donation payment details.
317 $paypal_amount = $data['mc_gross'];
318 $payment_status = strtolower( $data['payment_status'] );
319 $currency_code = strtolower( $data['mc_currency'] );
320 $business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
321 $payment_meta = give_get_payment_meta( $payment_id );
322
323 // Must be a PayPal standard IPN.
324 if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) {
325 return;
326 }
327
328 // Verify payment recipient.
329 if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) {
330
331 give_record_gateway_error(
332 __( 'IPN Error', 'give' ),
333 sprintf( /* translators: %s: Paypal IPN response */
334 __( 'Invalid business email in IPN response. IPN data: %s', 'give' ),
335 json_encode( $data )
336 ),
337 $payment_id
338 );
339 give_update_payment_status( $payment_id, 'failed' );
340 give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) );
341
342 return;
343 }
344
345 // Verify payment currency.
346 if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) {
347
348 give_record_gateway_error(
349 __( 'IPN Error', 'give' ),
350 sprintf( /* translators: %s: Paypal IPN response */
351 __( 'Invalid currency in IPN response. IPN data: %s', 'give' ),
352 json_encode( $data )
353 ),
354 $payment_id
355 );
356 give_update_payment_status( $payment_id, 'failed' );
357 give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
358
359 return;
360 }
361
362 // Process refunds & reversed.
363 if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) {
364 give_process_paypal_refund( $data, $payment_id );
365
366 return;
367 }
368
369 // Only complete payments once.
370 if ( 'publish' === get_post_status( $payment_id ) ) {
371 return;
372 }
373
374 // Retrieve the total donation amount (before PayPal).
375 $payment_amount = give_donation_amount( $payment_id );
376
377 // Check that the donation PP and local db amounts match.
378 if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
379 // The prices don't match
380 give_record_gateway_error(
381 __( 'IPN Error', 'give' ),
382 sprintf( /* translators: %s: Paypal IPN response */
383 __( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ),
384 json_encode( $data )
385 ),
386 $payment_id
387 );
388 give_update_payment_status( $payment_id, 'failed' );
389 give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
390
391 return;
392 }
393
394 // Process completed donations.
395 if ( 'completed' === $payment_status || give_is_test_mode() ) {
396
397 give_insert_payment_note(
398 $payment_id,
399 sprintf( /* translators: %s: Paypal transaction ID */
400 __( 'PayPal Transaction ID: %s', 'give' ),
401 $data['txn_id']
402 )
403 );
404 give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
405 give_update_payment_status( $payment_id, 'publish' );
406
407 } elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) {
408
409 // Look for possible pending reasons, such as an eCheck.
410 $note = give_paypal_get_pending_donation_note( $data['pending_reason'] );
411
412 if ( ! empty( $note ) ) {
413 give_insert_payment_note( $payment_id, $note );
414 }
415 }
416
417 }
418
419 add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 );
420
421 /**
422 * Process PayPal IPN Refunds
423 *
424 * @param array $data IPN Data
425 * @param int $payment_id The payment ID.
426 *
427 * @return void
428 * @since 1.0
429 */
430 function give_process_paypal_refund( $data, $payment_id = 0 ) {
431
432 // Collect payment details.
433 if ( empty( $payment_id ) ) {
434 return;
435 }
436
437 // Only refund payments once.
438 if ( 'refunded' === get_post_status( $payment_id ) ) {
439 return;
440 }
441
442 $payment_amount = give_donation_amount( $payment_id );
443 $refund_amount = $data['payment_gross'] * - 1;
444
445 if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
446
447 give_insert_payment_note(
448 $payment_id,
449 sprintf( /* translators: %s: Paypal parent transaction ID */
450 __( 'Partial PayPal refund processed: %s', 'give' ),
451 $data['parent_txn_id']
452 )
453 );
454
455 return; // This is a partial refund
456
457 }
458
459 give_insert_payment_note(
460 $payment_id,
461 sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */
462 __( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ),
463 $data['parent_txn_id'],
464 $data['reason_code']
465 )
466 );
467 give_insert_payment_note(
468 $payment_id,
469 sprintf( /* translators: %s: Paypal transaction ID */
470 __( 'PayPal Refund Transaction ID: %s', 'give' ),
471 $data['txn_id']
472 )
473 );
474 give_update_payment_status( $payment_id, 'refunded' );
475 }
476
477 /**
478 * Get PayPal Redirect
479 *
480 * @param bool $ssl_check Is SSL?
481 *
482 * @return string
483 * @since 1.0
484 */
485 function give_get_paypal_redirect( $ssl_check = false ) {
486
487 if ( is_ssl() || ! $ssl_check ) {
488 $protocol = 'https://';
489 } else {
490 $protocol = 'http://';
491 }
492
493 // Check the current payment mode
494 if ( give_is_test_mode() ) {
495 // Test mode
496 $paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr';
497 } else {
498 // Live mode
499 $paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr';
500 }
501
502 return apply_filters( 'give_paypal_uri', $paypal_uri );
503 }
504
505 /**
506 * Set the Page Style for offsite PayPal page.
507 *
508 * @return string
509 * @since 1.0
510 */
511 function give_get_paypal_page_style() {
512 $page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
513
514 return apply_filters( 'give_paypal_page_style', $page_style );
515 }
516
517 /**
518 * PayPal Success Page
519 *
520 * Shows "Donation Processing" message for PayPal payments that are still pending on site return
521 *
522 * @param $content
523 *
524 * @return string
525 * @since 1.0
526 */
527 function give_paypal_success_page_content( $content ) {
528
529 if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
530 return $content;
531 }
532
533 $payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
534
535 if ( ! $payment_id ) {
536 $session = give_get_purchase_session();
537 $payment_id = give_get_donation_id_by_key( $session['purchase_key'] );
538 }
539
540 $payment = get_post( $payment_id );
541 if ( $payment && 'pending' === $payment->post_status ) {
542
543 // Payment is still pending so show processing indicator to fix the race condition.
544 ob_start();
545
546 give_get_template_part( 'payment', 'processing' );
547
548 $content = ob_get_clean();
549
550 }
551
552 return $content;
553
554 }
555
556 add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
557
558 /**
559 * Given a transaction ID, generate a link to the PayPal transaction ID details
560 *
561 * @param string $transaction_id The Transaction ID
562 * @param int $payment_id The payment ID for this transaction
563 *
564 * @return string A link to the PayPal transaction details
565 * @since 1.0
566 */
567 function give_paypal_link_transaction_id( $transaction_id, $payment_id ) {
568
569 $paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
570 $transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
571
572 return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
573
574 }
575
576 add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
577
578
579 /**
580 * Get pending donation note.
581 *
582 * @param $pending_reason
583 *
584 * @return string
585 * @since 1.6.3
586 */
587 function give_paypal_get_pending_donation_note( $pending_reason ) {
588
589 $note = '';
590
591 switch ( $pending_reason ) {
592
593 case 'echeck':
594 $note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' );
595 break;
596
597 case 'address':
598 $note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' );
599 break;
600
601 case 'intl':
602 $note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' );
603 break;
604
605 case 'multi-currency':
606 $note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' );
607 break;
608
609 case 'paymentreview':
610 case 'regulatory_review':
611 $note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' );
612 break;
613
614 case 'unilateral':
615 $note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
616 break;
617
618 case 'upgrade':
619 $note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' );
620 break;
621
622 case 'verify':
623 $note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' );
624 break;
625
626 case 'other':
627 $note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' );
628 break;
629
630 } // End switch().
631
632 return apply_filters( 'give_paypal_get_pending_donation_note', $note );
633
634 }
635
636 /**
637 * Build paypal url
638 *
639 * @param int $payment_id Payment ID
640 * @param array $payment_data Array of payment data.
641 *
642 * @return mixed|string
643 */
644 function give_build_paypal_url( $payment_id, $payment_data ) {
645 // Only send to PayPal if the pending payment is created successfully.
646 $listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
647
648 // Get the success url.
649 $return_url = add_query_arg(
650 [
651 'payment-confirmation' => 'paypal',
652 'payment-id' => $payment_id,
653 ],
654 give_get_success_page_uri()
655 );
656
657 // Get the PayPal redirect uri.
658 $paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
659
660 // Item name.
661 $item_name = give_payment_gateway_item_title( $payment_data );
662
663 // Setup PayPal API params.
664 $paypal_args = [
665 'business' => give_get_option( 'paypal_email', false ),
666 'first_name' => $payment_data['user_info']['first_name'],
667 'last_name' => $payment_data['user_info']['last_name'],
668 'email' => $payment_data['user_email'],
669 'invoice' => $payment_data['purchase_key'],
670 'amount' => $payment_data['price'],
671 'item_name' => stripslashes( $item_name ),
672 'no_shipping' => '1',
673 'shipping' => '0',
674 'no_note' => '1',
675 'currency_code' => give_get_currency( $payment_id, $payment_data ),
676 'charset' => get_bloginfo( 'charset' ),
677 'custom' => $payment_id,
678 'rm' => '2',
679 'return' => $return_url,
680 'cancel_return' => give_get_failed_transaction_uri(),
681 'notify_url' => $listener_url,
682 'page_style' => give_get_paypal_page_style(),
683 'cbt' => get_bloginfo( 'name' ),
684 'bn' => 'givewp_SP',
685 ];
686
687 // Add user address if present.
688 if ( ! empty( $payment_data['user_info']['address'] ) ) {
689 $default_address = [
690 'line1' => '',
691 'line2' => '',
692 'city' => '',
693 'state' => '',
694 'zip' => '',
695 'country' => '',
696 ];
697
698 $address = wp_parse_args( $payment_data['user_info']['address'], $default_address );
699
700 $paypal_args['address1'] = $address['line1'];
701 $paypal_args['address2'] = $address['line2'];
702 $paypal_args['city'] = $address['city'];
703 $paypal_args['state'] = $address['state'];
704 $paypal_args['zip'] = $address['zip'];
705 $paypal_args['country'] = $address['country'];
706 }
707
708 // Donations or regular transactions?
709 $paypal_args['cmd'] = give_get_paypal_button_type();
710
711 /**
712 * Filter the paypal redirect args.
713 *
714 * @param array $paypal_args PayPal Arguments.
715 * @param array $payment_data Payment Data.
716 *
717 * @since 1.8
718 */
719 $paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data );
720
721 // Build query.
722 $paypal_redirect .= http_build_query( $paypal_args );
723
724 // Fix for some sites that encode the entities.
725 $paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
726
727 return $paypal_redirect;
728 }
729
730
731 /**
732 * Get paypal button type.
733 *
734 * @return string
735 * @since 1.8
736 */
737 function give_get_paypal_button_type() {
738 // paypal_button_type can be donation or standard.
739 $paypal_button_type = '_donations';
740 if ( 'standard' === give_get_option( 'paypal_button_type' ) ) {
741 $paypal_button_type = '_xclick';
742 }
743
744 return $paypal_button_type;
745 }
746
747 /**
748 * Update Purchase key for specific gateway.
749 *
750 * @param string $custom_purchase_key
751 * @param string $gateway
752 * @param string $purchase_key
753 *
754 * @return string
755 * @since 2.2.4
756 */
757 function give_paypal_purchase_key( $custom_purchase_key, $gateway, $purchase_key ) {
758
759 if ( 'paypal' === $gateway ) {
760 $invoice_id_prefix = give_get_option( 'paypal_invoice_prefix', 'GIVE-' );
761 $custom_purchase_key = $invoice_id_prefix . $purchase_key;
762 }
763
764 return $custom_purchase_key;
765 }
766
767 add_filter( 'give_donation_purchase_key', 'give_paypal_purchase_key', 10, 3 );
768
769
770 /**
771 * PayPal Standard Connect button.
772 *
773 * This uses Stripe's Connect button but swaps the link and logo with PayPal's.
774 *
775 * @return string
776 * @since 2.5.0
777 */
778 function give_paypal_connect_button() {
779
780 // Prepare Stripe Connect URL.
781 $link = admin_url( 'edit.php?post_type=give_forms&page=give-settings&tab=gateways&section=paypal-standard' );
782
783 return sprintf(
784 '<a href="%1$s" id="give-paypal-connect"><span>%2$s</span></a>',
785 esc_url( $link ),
786 esc_html__( 'Connect to PayPal', 'give' )
787 );
788 }
789