PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.18.0
GiveWP – Donation Plugin and Fundraising Platform v4.18.0
4.18.0 4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 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 All 257 releases
give / src / LegacyPaymentGateways / Adapters / LegacyPaymentGatewayAdapter.php

LegacyPaymentGatewayAdapter.php in GiveWP – Donation Plugin and Fundraising Platform 4.18.0, at src/LegacyPaymentGateways/Adapters/LegacyPaymentGatewayAdapter.php

460 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\LegacyPaymentGateways\Adapters;
4
5 use Exception;
6 use Give\DonationForms\Actions\GetOrCreateDonor;
7 use Give\DonationForms\V2\Models\DonationForm;
8 use Give\Donations\Models\Donation;
9 use Give\Donations\ValueObjects\DonationType;
10 use Give\Donors\Models\Donor;
11 use Give\Framework\PaymentGateways\Contracts\PaymentGatewayInterface;
12 use Give\Framework\PaymentGateways\Controllers\GatewayPaymentController;
13 use Give\Framework\PaymentGateways\Controllers\GatewaySubscriptionController;
14 use Give\Framework\PaymentGateways\Log\PaymentGatewayLog;
15 use Give\Framework\PaymentGateways\PaymentGateway;
16 use Give\Framework\PaymentGateways\Traits\HandleHttpResponses;
17 use Give\Helpers\Form\Utils;
18 use Give\PaymentGateways\Actions\GetGatewayDataFromRequest;
19 use Give\PaymentGateways\DataTransferObjects\FormData;
20 use Give\PaymentGateways\DataTransferObjects\SubscriptionData;
21 use Give\Session\SessionDonation\DonationAccessor;
22 use Give\Subscriptions\Models\Subscription;
23 use Give\Subscriptions\ValueObjects\SubscriptionMode;
24 use Give\Subscriptions\ValueObjects\SubscriptionPeriod;
25 use Give\Subscriptions\ValueObjects\SubscriptionStatus;
26
27 /**
28 * Class LegacyPaymentGatewayAdapter
29 * @since 2.18.0
30 */
31 class LegacyPaymentGatewayAdapter
32 {
33 use HandleHttpResponses;
34
35 /**
36 * Get legacy form field markup to display gateway specific payment fields
37 *
38 * @since 2.18.0
39 * @since 2.19.0 Added missing $args parameter for ID prefixing and general backwards compatibility.
40 */
41 public function getLegacyFormFieldMarkup(
42 int $formId,
43 array $args,
44 PaymentGateway $registeredGateway
45 ): string {
46 return $registeredGateway->getLegacyFormFieldMarkup($formId, $args);
47 }
48
49 /**
50 * First we create a payment, then move on to the gateway processing
51 *
52 * @since 3.9.0 Add support to "phone" property
53 * @since 3.2.0 Capture exceptions when get gateway data.
54 * @since 3.0.0 Catch and handle errors from the gateway here
55 * @since 2.30.0 Add success, cancel and failed URLs to gateway data. This will be used in both v2 and v3 forms so gateways can just refer to the gateway data.
56 * @since 2.24.0 add support for payment mode
57 * @since 2.21.0 Replace give_insert_payment with donation model. Store legacy subscription data in donation meta.
58 * Attach subscription id to donation.
59 * @since 2.19.0 Replace is_recurring with is_donation_recurring to detect recurring donations.
60 * @since 2.18.0
61 *
62 * @throws Exception
63 */
64 public function handleBeforeGateway(array $legacyDonationData, PaymentGateway $registeredGateway)
65 {
66 $formData = FormData::fromRequest($legacyDonationData);
67
68 $this->validateGatewayNonce($formData->gatewayNonce);
69
70 $this->validateDonationFormStatus($formData->formId);
71
72 /**
73 * Fires at the start of donation form processing, before any data is processed.
74 *
75 * @since 3.4.0
76 *
77 * @param FormData $formData
78 * @param string $gatewayId
79 */
80 do_action('give_donation_form_processing_start', $formData, $formData->paymentGateway);
81
82 $donor = $this->getOrCreateDonor(
83 $formData->formId,
84 $formData->donorInfo->wpUserId,
85 $formData->donorInfo->email,
86 $formData->donorInfo->firstName,
87 $formData->donorInfo->lastName,
88 $formData->donorInfo->honorific,
89 ''
90 );
91
92 $donation = $formData->toDonation($donor->id);
93
94 if (give_recurring_is_donation_recurring($legacyDonationData)) {
95 $subscriptionData = SubscriptionData::fromRequest($legacyDonationData);
96
97 $paymentMode = !empty($legacyDonationData['payment_mode']) ? new SubscriptionMode(
98 $legacyDonationData['payment_mode']
99 ) : null;
100
101 if ($paymentMode === null) {
102 $paymentMode = give_is_test_mode() ? SubscriptionMode::TEST() : SubscriptionMode::LIVE();
103 }
104
105 $subscription = Subscription::create([
106 'amount' => $donation->amount,
107 'period' => new SubscriptionPeriod($subscriptionData->period),
108 'frequency' => (int)$subscriptionData->frequency,
109 'donorId' => $donor->id,
110 'installments' => (int)$subscriptionData->times,
111 'status' => SubscriptionStatus::PENDING(),
112 'mode' => $paymentMode,
113 'donationFormId' => $formData->formId,
114 ]);
115
116 $donation->type = DonationType::SUBSCRIPTION();
117 $donation->subscriptionId = $subscription->id;
118 $donation->save();
119
120 give()->subscriptions->updateLegacyParentPaymentId($subscription->id, $donation->id);
121
122 $this->setSession($donation->id);
123
124 /**
125 * Fires after a donation is created during donation form processing.
126 *
127 * @since 3.4.0
128 *
129 * @param Donation $donation
130 * @param Subscription|null $subscription
131 */
132 do_action('givewp_donation_form_processing_donation_created', $donation, $subscription);
133
134 /**
135 * Fires after a subscription is created during donation form processing.
136 *
137 * @since 3.4.0
138 *
139 * @param Subscription $subscription
140 * @param Donation $donation
141 */
142 do_action('givewp_donation_form_processing_subscription_created', $subscription, $donation);
143
144 try {
145 /**
146 * Filter hook to provide gateway data before initial transaction for subscription is processed by the gateway.
147 *
148 * @since 2.21.2
149 */
150 $gatewayData = apply_filters(
151 "givewp_create_subscription_gateway_data_{$registeredGateway::id()}",
152 (new GetGatewayDataFromRequest())(),
153 $donation,
154 $subscription
155 );
156
157 $gatewayData = $this->addUrlsToGatewayData($donation, $gatewayData, $registeredGateway);
158
159 $controller = new GatewaySubscriptionController($registeredGateway);
160
161 $controller->create($donation, $subscription, $gatewayData);
162 } catch (Exception $exception) {
163 PaymentGatewayLog::error(
164 $exception->getMessage(),
165 [
166 'Payment Gateway' => $registeredGateway::id(),
167 'Donation' => $donation->toArray(),
168 'Subscription' => $subscription->toArray(),
169 ]
170 );
171
172 $message = __(
173 'An unexpected error occurred while processing the subscription. Please try again or contact the site administrator.',
174 'give'
175 );
176
177 $this->handleExceptionResponse($exception, $message);
178 }
179 } else {
180 $donation->type = DonationType::SINGLE();
181 $donation->save();
182
183 $this->setSession($donation->id);
184
185 /**
186 * Fires after a donation is created during donation form processing.
187 *
188 * @since 3.4.0
189 *
190 * @param Donation $donation
191 * @param Subscription|null $subscription
192 */
193 do_action('givewp_donation_form_processing_donation_created', $donation, null);
194
195 try {
196 /**
197 * Filter hook to provide gateway data before transaction is processed by the gateway.
198 *
199 * @since 2.21.2
200 */
201 $gatewayData = apply_filters(
202 "givewp_create_payment_gateway_data_{$registeredGateway::id()}",
203 (new GetGatewayDataFromRequest())(),
204 $donation
205 );
206
207 $gatewayData = $this->addUrlsToGatewayData($donation, $gatewayData, $registeredGateway);
208
209 $controller = new GatewayPaymentController($registeredGateway);
210
211 $controller->create($donation, $gatewayData);
212 } catch (Exception $exception) {
213 PaymentGatewayLog::error(
214 $exception->getMessage(),
215 [
216 'Payment Gateway' => $registeredGateway::id(),
217 'Donation' => $donation->toArray(),
218 ]
219 );
220
221 $message = __(
222 'An unexpected error occurred while processing the donation. Please try again or contact the site administrator.',
223 'give'
224 );
225
226 $this->handleExceptionResponse($exception, $message);
227 }
228 }
229 }
230
231 /**
232 * @since 2.30.0
233 */
234 protected function getGatewayDataSuccessUrl(int $donationId): string
235 {
236 $formId = give_get_payment_form_id($donationId);
237 $isEmbedDonationForm = !Utils::isLegacyForm($formId);
238 $donationFormPageUrl = (new DonationAccessor())->get()->formEntry->currentUrl ?: get_permalink($formId);
239
240 return $isEmbedDonationForm ?
241 Utils::createSuccessPageURL($donationFormPageUrl) :
242 give_get_success_page_url();
243 }
244
245 /**
246 * @since 2.30.0
247 */
248 protected function getGatewayDataFailedUrl(int $donationId): string
249 {
250 $formId = give_get_payment_form_id($donationId);
251 $isEmbedDonationForm = !Utils::isLegacyForm($formId);
252 $donationFormPageUrl = (new DonationAccessor())->get()->formEntry->currentUrl ?: get_permalink($formId);
253
254 return $isEmbedDonationForm ?
255 Utils::createFailedPageURL($donationFormPageUrl) :
256 give_get_failed_transaction_uri();
257 }
258
259 /**
260 * @since 2.30.0
261 */
262 protected function getGatewayDataCancelUrl(int $donationId): string
263 {
264 $formId = give_get_payment_form_id($donationId);
265
266 return (new DonationAccessor())->get()->formEntry->currentUrl ?: get_permalink($formId);
267 }
268
269 /**
270 * Validate Gateway Nonce
271 *
272 * @since 2.18.0
273 */
274 private function validateGatewayNonce(string $gatewayNonce)
275 {
276 if (!wp_verify_nonce($gatewayNonce, 'give-gateway')) {
277 wp_die(
278 esc_html__(
279 'We\'re unable to recognize your session. Please refresh the screen to try again; otherwise contact your website administrator for assistance.',
280 'give'
281 ),
282 esc_html__('Error', 'give'),
283 ['response' => 403]
284 );
285 }
286 }
287
288 /**
289 * Validate Donation Form Status
290 *
291 * @since 2.33.2
292 */
293 private function validateDonationFormStatus(int $formId)
294 {
295 $donationForm = DonationForm::find($formId);
296
297 if (!$donationForm || $donationForm->status->isTrash()) {
298 wp_die(
299 esc_html__(
300 'This donation form is not accepting donations.',
301 'give'
302 ),
303 esc_html__('Error', 'give'),
304 ['response' => 403]
305 );
306 }
307 }
308
309 /**
310 * Set donation id to purchase session for use in the donation receipt.
311 *
312 * @since 2.21.0
313 *
314 * @param $donationId
315 *
316 * @return void
317 */
318 private function setSession($donationId)
319 {
320 $purchaseSession = (array)give()->session->get('give_purchase');
321
322 if ($purchaseSession && array_key_exists('purchase_key', $purchaseSession)) {
323 $purchaseSession['donation_id'] = $donationId;
324 give()->session->set('give_purchase', $purchaseSession);
325 }
326 }
327
328 /**
329 * @since 3.9.0 Add support to "phone" property
330 * @since 3.4.0 add $formId and do_action
331 * @since 3.2.0 add honorific and use GetOrCreateDonor action
332 * @since 2.21.0
333 *
334 * @throws Exception
335 */
336 private function getOrCreateDonor(
337 ?int $formId,
338 ?int $userId,
339 string $donorEmail,
340 string $firstName,
341 string $lastName,
342 ?string $honorific,
343 ?string $donorPhone
344 ): Donor {
345 $getOrCreateDonorAction = new GetOrCreateDonor();
346
347 $donor = $getOrCreateDonorAction(
348 $userId,
349 $donorEmail,
350 $firstName,
351 $lastName,
352 $honorific,
353 $donorPhone
354 );
355
356 if ($getOrCreateDonorAction->donorCreated) {
357 /**
358 * Fires after a donor is created during donation form processing.
359 *
360 * @since 3.4.0
361 *
362 * @param Donor $donor
363 * @param int $formId
364 */
365 do_action('givewp_donation_form_processing_donor_created', $donor, $formId);
366 }
367
368 return $donor;
369 }
370
371 /**
372 * @since 2.29.0
373 */
374 public function addOptRefundCheckbox(int $donationId, PaymentGatewayInterface $registeredGateway)
375 {
376 $donation = Donation::find($donationId);
377 if ($donation->gatewayId === $registeredGateway::id()) {
378 ?>
379 <div id="give-gateway-opt-refund-wrap"
380 class="give-gateway-opt-refund give-admin-box-inside give-hidden">
381 <p>
382 <input type="checkbox" id="give-gateway-opt-refund" name="give_gateway_opt_refund" value="1" />
383 <label for="give-gateway-opt-refund">
384 <?php
385 echo sprintf(esc_html__('Refund the donation at %s?', 'give'), esc_html($registeredGateway->getName()));
386 ?>
387 </label>
388 </p>
389 </div>
390 <script>
391 if (!!document.getElementById("give-payment-status") &&
392 1 === document.querySelectorAll("div.give-admin-box > div.give-hidden[id*=\"opt-refund\"] input[type=\"checkbox\"]").length
393 ) {
394 document.getElementById("give-payment-status").addEventListener("change", function(event) {
395 const refundCheckbox = document.getElementById("give-gateway-opt-refund");
396
397 if (null === refundCheckbox) {
398 return;
399 }
400
401 refundCheckbox.checked = false;
402
403 if ("refunded" === event.target.value) {
404 document.getElementById("give-gateway-opt-refund-wrap").style.display = "block";
405 } else {
406 document.getElementById("give-gateway-opt-refund-wrap").style.display = "none";
407 }
408 });
409 }
410 </script>
411 <?php
412 }
413 }
414
415 /**
416 * @since 2.29.0
417 */
418 public function maybeRefundOnGateway(
419 int $donationId,
420 string $newStatus,
421 string $oldStatus,
422 PaymentGateway $registeredGateway
423 ) {
424 $gatewayOptRefund = !empty($_POST['give_gateway_opt_refund']) ? give_clean(
425 $_POST['give_gateway_opt_refund']
426 ) : '';
427 $canProcessRefund = !empty($gatewayOptRefund) ? $gatewayOptRefund : false;
428
429 // Only move forward if refund requested.
430 if (!$canProcessRefund) {
431 return;
432 }
433
434 $donation = Donation::find($donationId);
435 if (
436 $donation->gatewayId === $registeredGateway::id() &&
437 'refunded' === $newStatus &&
438 'refunded' !== $oldStatus
439 ) {
440 $controller = new GatewayPaymentController($registeredGateway);
441 $controller->refund($donation);
442 }
443 }
444
445 /**
446 * @since 2.30.0
447 */
448 protected function addUrlsToGatewayData(Donation $donation, $gatewayData, PaymentGateway $registeredGateway)
449 {
450 return array_merge($gatewayData, [
451 'successUrl' => add_query_arg(
452 ['payment-confirmation' => $registeredGateway::id()],
453 $this->getGatewayDataSuccessUrl($donation->id)
454 ),
455 'cancelUrl' => $this->getGatewayDataCancelUrl($donation->id),
456 'failedUrl' => $this->getGatewayDataFailedUrl($donation->id)
457 ]);
458 }
459 }
460