PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.18.0.1
GiveWP – Donation Plugin and Fundraising Platform v4.18.0.1
4.18.0.1 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 All 258 releases
give / src / PaymentGateways / Gateways / PayPalStandard / Actions / ProcessIpnDonationRefund.php

ProcessIpnDonationRefund.php in GiveWP – Donation Plugin and Fundraising Platform 4.18.0.1, at src/PaymentGateways/Gateways/PayPalStandard/Actions/ProcessIpnDonationRefund.php

118 lines 3.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\PaymentGateways\Gateways\PayPalStandard\Actions;
4
5 use Exception;
6 use Give\Donations\Models\Donation;
7 use Give\Donations\Models\DonationNote;
8 use Give\Donations\ValueObjects\DonationStatus;
9 use Give\Framework\Support\ValueObjects\Money;
10 use Give\Log\Log;
11 use stdClass;
12
13 /**
14 * @since 2.19.0
15 */
16 class ProcessIpnDonationRefund
17 {
18 /**
19 * @since 4.18.0.1 Use the Donation model and skip refunds with a missing, non-negative, or over-total amount.
20 * @since 2.19.0
21 *
22 * @param stdClass $ipnEventData PayPal IPN data.
23 * @param int $donationId ID of the donation being refunded.
24 *
25 * @return void
26 */
27 public function __invoke(stdClass $ipnEventData, $donationId)
28 {
29 $donation = Donation::find($donationId);
30
31 if ( ! $donation) {
32 return;
33 }
34
35 $refundedAmount = $this->getRefundedAmount($ipnEventData, $donation->amount->getCurrency()->getCode());
36
37 if ( ! $refundedAmount || ! $this->isValidRefundAmount($refundedAmount, $donation->amount)) {
38 Log::error(
39 'PayPal Standard IPN Error',
40 [
41 'Message' => sprintf(
42 'Refund amount (%s) for donation #%d is not valid.',
43 $ipnEventData->mc_gross ?? '(not set)',
44 $donationId
45 ),
46 'Event Data' => $ipnEventData,
47 ]
48 );
49
50 return;
51 }
52
53 if ($refundedAmount->absolute()->lessThan($donation->amount)) {
54 DonationNote::create([
55 'donationId' => $donation->id,
56 'content' => sprintf( /* translators: %s: Paypal parent transaction ID */
57 __('Partial PayPal refund processed: %s', 'give'),
58 $ipnEventData->parent_txn_id
59 ),
60 ]);
61
62 return;
63 }
64
65 DonationNote::create([
66 'donationId' => $donation->id,
67 'content' => sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */
68 __('PayPal Payment #%1$s Refunded for reason: %2$s', 'give'),
69 $ipnEventData->parent_txn_id,
70 $ipnEventData->reason_code
71 ),
72 ]);
73
74 DonationNote::create([
75 'donationId' => $donation->id,
76 'content' => sprintf( /* translators: %s: Paypal transaction ID */
77 __('PayPal Refund Transaction ID: %s', 'give'),
78 $ipnEventData->txn_id
79 ),
80 ]);
81
82 $donation->status = DonationStatus::REFUNDED();
83 $donation->save();
84 }
85
86 /**
87 * @since 4.18.0.1
88 *
89 * @param stdClass $ipnEventData PayPal IPN data.
90 * @param string $currency Donation currency code.
91 *
92 * @return Money|null The IPN mc_gross in the donation currency, or null when it is missing or not a decimal amount.
93 */
94 private function getRefundedAmount(stdClass $ipnEventData, string $currency): ?Money
95 {
96 try {
97 return Money::fromDecimal($ipnEventData->mc_gross ?? '', $currency);
98 } catch (Exception $e) {
99 return null;
100 }
101 }
102
103 /**
104 * PayPal Standard sends refunds as a negative amount that cannot exceed the donation total.
105 *
106 * @since 4.18.0.1
107 *
108 * @param Money $refundedAmount IPN mc_gross amount.
109 * @param Money $donationAmount Donation total.
110 *
111 * @return bool
112 */
113 private function isValidRefundAmount(Money $refundedAmount, Money $donationAmount): bool
114 {
115 return $refundedAmount->isNegative() && $refundedAmount->absolute()->lessThanOrEqual($donationAmount);
116 }
117 }
118