@@ -23,174 +23,174 @@ |
||
23 | 23 | class RegistrationPayments |
24 | 24 | { |
25 | 25 | |
26 | - /** |
|
27 | - * update registrations REG_paid field after successful payment and link registrations with payment |
|
28 | - * |
|
29 | - * @param EE_Transaction $transaction |
|
30 | - * @param EE_Payment $payment |
|
31 | - * @param EE_Registration[] $registrations |
|
32 | - * @throws EE_Error |
|
33 | - * @throws ReflectionException |
|
34 | - */ |
|
35 | - public function processRegistrationPayments( |
|
36 | - EE_Transaction $transaction, |
|
37 | - EE_Payment $payment, |
|
38 | - array $registrations = [] |
|
39 | - ) { |
|
40 | - // only process if payment was successful |
|
41 | - if ($payment->status() !== EEM_Payment::status_id_approved) { |
|
42 | - return; |
|
43 | - } |
|
44 | - $registrations = $this->loadRegistrationsIfMissing($transaction, $registrations); |
|
45 | - // still nothing ??!?? |
|
46 | - if (empty($registrations)) { |
|
47 | - return; |
|
48 | - } |
|
49 | - // todo: break out the following logic into a separate strategy class |
|
50 | - // todo: named something like "Sequential_Reg_Payment_Strategy" |
|
51 | - // todo: which would apply payments using the capitalist "first come first paid" approach |
|
52 | - // todo: then have another strategy class like "Distributed_Reg_Payment_Strategy" |
|
53 | - // todo: which would be the socialist "everybody gets a piece of pie" approach, |
|
54 | - // todo: which would be better for deposits, where you want a bit of the payment applied to each registration |
|
55 | - $available_payment_amount = $this->sequentialRegPaymentStrategy($payment, $registrations); |
|
56 | - $this->remainderNotice($payment, $available_payment_amount, $registrations); |
|
57 | - } |
|
26 | + /** |
|
27 | + * update registrations REG_paid field after successful payment and link registrations with payment |
|
28 | + * |
|
29 | + * @param EE_Transaction $transaction |
|
30 | + * @param EE_Payment $payment |
|
31 | + * @param EE_Registration[] $registrations |
|
32 | + * @throws EE_Error |
|
33 | + * @throws ReflectionException |
|
34 | + */ |
|
35 | + public function processRegistrationPayments( |
|
36 | + EE_Transaction $transaction, |
|
37 | + EE_Payment $payment, |
|
38 | + array $registrations = [] |
|
39 | + ) { |
|
40 | + // only process if payment was successful |
|
41 | + if ($payment->status() !== EEM_Payment::status_id_approved) { |
|
42 | + return; |
|
43 | + } |
|
44 | + $registrations = $this->loadRegistrationsIfMissing($transaction, $registrations); |
|
45 | + // still nothing ??!?? |
|
46 | + if (empty($registrations)) { |
|
47 | + return; |
|
48 | + } |
|
49 | + // todo: break out the following logic into a separate strategy class |
|
50 | + // todo: named something like "Sequential_Reg_Payment_Strategy" |
|
51 | + // todo: which would apply payments using the capitalist "first come first paid" approach |
|
52 | + // todo: then have another strategy class like "Distributed_Reg_Payment_Strategy" |
|
53 | + // todo: which would be the socialist "everybody gets a piece of pie" approach, |
|
54 | + // todo: which would be better for deposits, where you want a bit of the payment applied to each registration |
|
55 | + $available_payment_amount = $this->sequentialRegPaymentStrategy($payment, $registrations); |
|
56 | + $this->remainderNotice($payment, $available_payment_amount, $registrations); |
|
57 | + } |
|
58 | 58 | |
59 | 59 | |
60 | - /** |
|
61 | - * @param EE_Transaction $transaction |
|
62 | - * @param array $registrations |
|
63 | - * @return array |
|
64 | - * @throws EE_Error |
|
65 | - * @throws ReflectionException |
|
66 | - */ |
|
67 | - private function loadRegistrationsIfMissing(EE_Transaction $transaction, array $registrations = []): array |
|
68 | - { |
|
69 | - if (empty($registrations)) { |
|
70 | - // find registrations with monies owing that can receive a payment |
|
71 | - $registrations = $transaction->registrations( |
|
72 | - [ |
|
73 | - [ |
|
74 | - // only these reg statuses can receive payments |
|
75 | - 'STS_ID' => ['IN', EEM_Registration::reg_statuses_that_allow_payment()], |
|
76 | - 'REG_final_price' => ['!=', 0], |
|
77 | - 'REG_final_price*' => ['!=', 'REG_paid', true], |
|
78 | - ], |
|
79 | - ] |
|
80 | - ); |
|
81 | - } |
|
82 | - return $registrations; |
|
83 | - } |
|
60 | + /** |
|
61 | + * @param EE_Transaction $transaction |
|
62 | + * @param array $registrations |
|
63 | + * @return array |
|
64 | + * @throws EE_Error |
|
65 | + * @throws ReflectionException |
|
66 | + */ |
|
67 | + private function loadRegistrationsIfMissing(EE_Transaction $transaction, array $registrations = []): array |
|
68 | + { |
|
69 | + if (empty($registrations)) { |
|
70 | + // find registrations with monies owing that can receive a payment |
|
71 | + $registrations = $transaction->registrations( |
|
72 | + [ |
|
73 | + [ |
|
74 | + // only these reg statuses can receive payments |
|
75 | + 'STS_ID' => ['IN', EEM_Registration::reg_statuses_that_allow_payment()], |
|
76 | + 'REG_final_price' => ['!=', 0], |
|
77 | + 'REG_final_price*' => ['!=', 'REG_paid', true], |
|
78 | + ], |
|
79 | + ] |
|
80 | + ); |
|
81 | + } |
|
82 | + return $registrations; |
|
83 | + } |
|
84 | 84 | |
85 | 85 | |
86 | - /** |
|
87 | - * @param EE_Payment $payment |
|
88 | - * @param array $registrations |
|
89 | - * @return float |
|
90 | - * @throws EE_Error |
|
91 | - * @throws ReflectionException |
|
92 | - */ |
|
93 | - private function sequentialRegPaymentStrategy(EE_Payment $payment, array $registrations): float |
|
94 | - { |
|
95 | - $refund = $payment->is_a_refund(); |
|
96 | - // how much is available to apply to registrations? |
|
97 | - $available_payment_amount = (float) abs($payment->amount()); |
|
98 | - foreach ($registrations as $registration) { |
|
99 | - if ($registration instanceof EE_Registration) { |
|
100 | - // nothing left? |
|
101 | - if ($available_payment_amount <= 0) { |
|
102 | - break; |
|
103 | - } |
|
104 | - if ($refund) { |
|
105 | - $available_payment_amount = $this->processRegistrationRefund( |
|
106 | - $registration, |
|
107 | - $payment, |
|
108 | - $available_payment_amount |
|
109 | - ); |
|
110 | - } else { |
|
111 | - $available_payment_amount = $this->processRegistrationPayment( |
|
112 | - $registration, |
|
113 | - $payment, |
|
114 | - $available_payment_amount |
|
115 | - ); |
|
116 | - } |
|
117 | - } |
|
118 | - } |
|
119 | - return $available_payment_amount; |
|
120 | - } |
|
86 | + /** |
|
87 | + * @param EE_Payment $payment |
|
88 | + * @param array $registrations |
|
89 | + * @return float |
|
90 | + * @throws EE_Error |
|
91 | + * @throws ReflectionException |
|
92 | + */ |
|
93 | + private function sequentialRegPaymentStrategy(EE_Payment $payment, array $registrations): float |
|
94 | + { |
|
95 | + $refund = $payment->is_a_refund(); |
|
96 | + // how much is available to apply to registrations? |
|
97 | + $available_payment_amount = (float) abs($payment->amount()); |
|
98 | + foreach ($registrations as $registration) { |
|
99 | + if ($registration instanceof EE_Registration) { |
|
100 | + // nothing left? |
|
101 | + if ($available_payment_amount <= 0) { |
|
102 | + break; |
|
103 | + } |
|
104 | + if ($refund) { |
|
105 | + $available_payment_amount = $this->processRegistrationRefund( |
|
106 | + $registration, |
|
107 | + $payment, |
|
108 | + $available_payment_amount |
|
109 | + ); |
|
110 | + } else { |
|
111 | + $available_payment_amount = $this->processRegistrationPayment( |
|
112 | + $registration, |
|
113 | + $payment, |
|
114 | + $available_payment_amount |
|
115 | + ); |
|
116 | + } |
|
117 | + } |
|
118 | + } |
|
119 | + return $available_payment_amount; |
|
120 | + } |
|
121 | 121 | |
122 | 122 | |
123 | - /** |
|
124 | - * @throws EE_Error |
|
125 | - */ |
|
126 | - private function remainderNotice(EE_Payment $payment, float $available_payment_amount, array $registrations) |
|
127 | - { |
|
128 | - if ( |
|
129 | - $available_payment_amount > 0 |
|
130 | - && apply_filters( |
|
131 | - 'FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', |
|
132 | - false |
|
133 | - ) |
|
134 | - ) { |
|
135 | - EE_Error::add_attention( |
|
136 | - sprintf( |
|
137 | - esc_html__( |
|
138 | - 'A remainder of %1$s exists after applying this payment to Registration(s) %2$s.%3$sPlease verify that the original payment amount of %4$s is correct. If so, you should edit this payment and select at least one additional registration in the "Registrations to Apply Payment to" section, so that the remainder of this payment can be applied to the additional registration(s).', |
|
139 | - 'event_espresso' |
|
140 | - ), |
|
141 | - EEH_Template::format_currency($available_payment_amount), |
|
142 | - implode(', ', array_keys($registrations)), |
|
143 | - '<br/>', |
|
144 | - EEH_Template::format_currency($payment->amount()) |
|
145 | - ), |
|
146 | - __FILE__, |
|
147 | - __FUNCTION__, |
|
148 | - __LINE__ |
|
149 | - ); |
|
150 | - } |
|
151 | - } |
|
123 | + /** |
|
124 | + * @throws EE_Error |
|
125 | + */ |
|
126 | + private function remainderNotice(EE_Payment $payment, float $available_payment_amount, array $registrations) |
|
127 | + { |
|
128 | + if ( |
|
129 | + $available_payment_amount > 0 |
|
130 | + && apply_filters( |
|
131 | + 'FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', |
|
132 | + false |
|
133 | + ) |
|
134 | + ) { |
|
135 | + EE_Error::add_attention( |
|
136 | + sprintf( |
|
137 | + esc_html__( |
|
138 | + 'A remainder of %1$s exists after applying this payment to Registration(s) %2$s.%3$sPlease verify that the original payment amount of %4$s is correct. If so, you should edit this payment and select at least one additional registration in the "Registrations to Apply Payment to" section, so that the remainder of this payment can be applied to the additional registration(s).', |
|
139 | + 'event_espresso' |
|
140 | + ), |
|
141 | + EEH_Template::format_currency($available_payment_amount), |
|
142 | + implode(', ', array_keys($registrations)), |
|
143 | + '<br/>', |
|
144 | + EEH_Template::format_currency($payment->amount()) |
|
145 | + ), |
|
146 | + __FILE__, |
|
147 | + __FUNCTION__, |
|
148 | + __LINE__ |
|
149 | + ); |
|
150 | + } |
|
151 | + } |
|
152 | 152 | |
153 | 153 | |
154 | - /** |
|
155 | - * update registration REG_paid field after successful payment and link registration with payment |
|
156 | - * |
|
157 | - * @param EE_Registration $registration |
|
158 | - * @param EE_Payment $payment |
|
159 | - * @param float $available_payment_amount |
|
160 | - * @return float |
|
161 | - * @throws EE_Error |
|
162 | - * @throws ReflectionException |
|
163 | - * @throws ReflectionException |
|
164 | - */ |
|
165 | - public function processRegistrationPayment( |
|
166 | - EE_Registration $registration, |
|
167 | - EE_Payment $payment, |
|
168 | - float $available_payment_amount = 0.00 |
|
169 | - ): float { |
|
170 | - // update $available_payment_amount |
|
171 | - $available_payment_amount -= $registration->applyPayment($payment, $available_payment_amount); |
|
172 | - return $available_payment_amount; |
|
173 | - } |
|
154 | + /** |
|
155 | + * update registration REG_paid field after successful payment and link registration with payment |
|
156 | + * |
|
157 | + * @param EE_Registration $registration |
|
158 | + * @param EE_Payment $payment |
|
159 | + * @param float $available_payment_amount |
|
160 | + * @return float |
|
161 | + * @throws EE_Error |
|
162 | + * @throws ReflectionException |
|
163 | + * @throws ReflectionException |
|
164 | + */ |
|
165 | + public function processRegistrationPayment( |
|
166 | + EE_Registration $registration, |
|
167 | + EE_Payment $payment, |
|
168 | + float $available_payment_amount = 0.00 |
|
169 | + ): float { |
|
170 | + // update $available_payment_amount |
|
171 | + $available_payment_amount -= $registration->applyPayment($payment, $available_payment_amount); |
|
172 | + return $available_payment_amount; |
|
173 | + } |
|
174 | 174 | |
175 | 175 | |
176 | - /** |
|
177 | - * update registration REG_paid field after refund and link registration with payment |
|
178 | - * |
|
179 | - * @param EE_Registration $registration |
|
180 | - * @param EE_Payment $payment |
|
181 | - * @param float $available_refund_amount - IMPORTANT !!! SEND AVAILABLE REFUND AMOUNT AS A POSITIVE NUMBER |
|
182 | - * @return float |
|
183 | - * @throws EE_Error |
|
184 | - * @throws ReflectionException |
|
185 | - * @throws ReflectionException |
|
186 | - */ |
|
187 | - public function processRegistrationRefund( |
|
188 | - EE_Registration $registration, |
|
189 | - EE_Payment $payment, |
|
190 | - float $available_refund_amount = 0.00 |
|
191 | - ): float { |
|
192 | - // update $available_payment_amount |
|
193 | - $available_refund_amount -= $registration->applyPayment($payment, $available_refund_amount); |
|
194 | - return $available_refund_amount; |
|
195 | - } |
|
176 | + /** |
|
177 | + * update registration REG_paid field after refund and link registration with payment |
|
178 | + * |
|
179 | + * @param EE_Registration $registration |
|
180 | + * @param EE_Payment $payment |
|
181 | + * @param float $available_refund_amount - IMPORTANT !!! SEND AVAILABLE REFUND AMOUNT AS A POSITIVE NUMBER |
|
182 | + * @return float |
|
183 | + * @throws EE_Error |
|
184 | + * @throws ReflectionException |
|
185 | + * @throws ReflectionException |
|
186 | + */ |
|
187 | + public function processRegistrationRefund( |
|
188 | + EE_Registration $registration, |
|
189 | + EE_Payment $payment, |
|
190 | + float $available_refund_amount = 0.00 |
|
191 | + ): float { |
|
192 | + // update $available_payment_amount |
|
193 | + $available_refund_amount -= $registration->applyPayment($payment, $available_refund_amount); |
|
194 | + return $available_refund_amount; |
|
195 | + } |
|
196 | 196 | } |
@@ -32,379 +32,379 @@ |
||
32 | 32 | */ |
33 | 33 | class IpnHandler |
34 | 34 | { |
35 | - private EEM_Payment_Method $payment_method_model; |
|
36 | - |
|
37 | - private EEM_Transaction $transaction_model; |
|
38 | - |
|
39 | - private EE_Core_Config $core_config; |
|
40 | - |
|
41 | - private EE_Organization_Config $organization; |
|
42 | - |
|
43 | - private PaymentProcessor $payment_processor; |
|
44 | - |
|
45 | - |
|
46 | - /** |
|
47 | - * @param EEM_Payment_Method $payment_method_model |
|
48 | - * @param EEM_Transaction $transaction_model |
|
49 | - * @param EE_Core_Config $core_config |
|
50 | - * @param EE_Organization_Config $organization |
|
51 | - * @param PaymentProcessor $payment_processor |
|
52 | - */ |
|
53 | - public function __construct( |
|
54 | - EEM_Payment_Method $payment_method_model, |
|
55 | - EEM_Transaction $transaction_model, |
|
56 | - EE_Core_Config $core_config, |
|
57 | - EE_Organization_Config $organization, |
|
58 | - PaymentProcessor $payment_processor |
|
59 | - ) { |
|
60 | - $this->payment_method_model = $payment_method_model; |
|
61 | - $this->transaction_model = $transaction_model; |
|
62 | - $this->core_config = $core_config; |
|
63 | - $this->organization = $organization; |
|
64 | - $this->payment_processor = $payment_processor; |
|
65 | - } |
|
66 | - |
|
67 | - |
|
68 | - /** |
|
69 | - * @param EE_Transaction $transaction |
|
70 | - * @param EE_Payment_Method $payment_method |
|
71 | - * @return string |
|
72 | - * @throws EE_Error |
|
73 | - * @throws ReflectionException |
|
74 | - */ |
|
75 | - public function getIpnUrlForPaymentMethod(EE_Transaction $transaction, EE_Payment_Method $payment_method): string |
|
76 | - { |
|
77 | - $primary_reg = $transaction->primary_registration(); |
|
78 | - if (! $primary_reg instanceof EE_Registration) { |
|
79 | - throw new EE_Error( |
|
80 | - sprintf( |
|
81 | - esc_html__( |
|
82 | - 'Cannot get IPN URL for transaction with ID %d because it has no primary registration', |
|
83 | - 'event_espresso' |
|
84 | - ), |
|
85 | - $transaction->ID() |
|
86 | - ) |
|
87 | - ); |
|
88 | - } |
|
89 | - $payment_method = $this->payment_method_model->ensure_is_obj( |
|
90 | - $payment_method, |
|
91 | - true |
|
92 | - ); |
|
93 | - return add_query_arg( |
|
94 | - [ |
|
95 | - 'e_reg_url_link' => $primary_reg->reg_url_link(), |
|
96 | - 'ee_payment_method' => $payment_method->slug(), |
|
97 | - ], |
|
98 | - $this->core_config->txn_page_url() |
|
99 | - ); |
|
100 | - } |
|
101 | - |
|
102 | - |
|
103 | - /** |
|
104 | - * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so |
|
105 | - * we can easily find what registration the IPN is for and what payment method. |
|
106 | - * However, if not, we'll give all payment methods a chance to claim it and process it. |
|
107 | - * If a payment is found for the IPN info, it is saved. |
|
108 | - * |
|
109 | - * @param array $request_data form post data |
|
110 | - * @param EE_Transaction|int|null $transaction optional (or a transaction's id) |
|
111 | - * @param EE_Payment_Method|int|string|null $payment_method optional (or a slug or id of PM) |
|
112 | - * @param bool $update_txn whether to call |
|
113 | - * EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() |
|
114 | - * @param bool $separate_IPN_request whether the IPN uses a separate request (true, |
|
115 | - * like PayPal) or is processed manually (false, |
|
116 | - * like Authorize.net) |
|
117 | - * @return EE_Payment |
|
118 | - * @throws EE_Error |
|
119 | - * @throws ReflectionException |
|
120 | - */ |
|
121 | - public function processIPN( |
|
122 | - array $request_data, |
|
123 | - $transaction = null, |
|
124 | - $payment_method = null, |
|
125 | - bool $update_txn = true, |
|
126 | - bool $separate_IPN_request = true |
|
127 | - ): EE_Payment { |
|
128 | - $request_data = $this->removeUnusableCharactersFromArray($request_data); |
|
129 | - EE_Processor_Base::set_IPN($separate_IPN_request); |
|
130 | - ['log' => $log, 'object' => $log_object] = $this->logIPN($request_data, $transaction, $payment_method); |
|
131 | - |
|
132 | - try { |
|
133 | - if ($transaction && $payment_method) { |
|
134 | - $payment = $this->processStandardIPN($request_data, $log, $log_object, $transaction, $payment_method); |
|
135 | - } else { |
|
136 | - ['payment' => $payment, 'payment_method' => $payment_method] = $this->processWaywardIPN( |
|
137 | - $request_data, |
|
138 | - $log_object |
|
139 | - ); |
|
140 | - } |
|
141 | - return $this->postIpnProcessing( |
|
142 | - $payment, |
|
143 | - $request_data, |
|
144 | - $update_txn, |
|
145 | - $separate_IPN_request, |
|
146 | - $transaction, |
|
147 | - $payment_method |
|
148 | - ); |
|
149 | - } catch (EE_Error $exception) { |
|
150 | - $this->invalidIpnException($exception, $transaction, $request_data); |
|
151 | - } |
|
152 | - } |
|
153 | - |
|
154 | - |
|
155 | - /** |
|
156 | - * @param array $request_data |
|
157 | - * @param EE_Change_Log $log |
|
158 | - * @param EE_Base_Class|null $log_object |
|
159 | - * @param EE_Transaction|int|null $transaction optional (model object or a transaction's id) |
|
160 | - * @param EE_Payment_Method|int|string|null $payment_method optional (model object or a slug or id of PM) |
|
161 | - * @return EE_Payment|null |
|
162 | - * @throws EE_Error |
|
163 | - * @throws ReflectionException |
|
164 | - */ |
|
165 | - private function processStandardIPN( |
|
166 | - array $request_data, |
|
167 | - EE_Change_Log $log, |
|
168 | - ?EE_Base_Class $log_object, |
|
169 | - $transaction = null, |
|
170 | - $payment_method = null |
|
171 | - ): ?EE_Payment { |
|
172 | - /** @type EE_Transaction $transaction */ |
|
173 | - $transaction = $this->transaction_model->ensure_is_obj($transaction); |
|
174 | - /** @type EE_Payment_Method $payment_method */ |
|
175 | - $payment_method = $this->payment_method_model->ensure_is_obj($payment_method); |
|
176 | - if (! $payment_method->type_obj() instanceof EE_PMT_Base) { |
|
177 | - // not a payment |
|
178 | - $this->invalidPaymentError(); |
|
179 | - return null; |
|
180 | - } |
|
181 | - try { |
|
182 | - $payment = $payment_method->type_obj()->handle_ipn($request_data, $transaction); |
|
183 | - $log->set_object($payment); |
|
184 | - return $payment; |
|
185 | - } catch (IpnException $exception) { |
|
186 | - $payment = $this->logIpnException($exception, $log_object); |
|
187 | - } |
|
188 | - return $payment; |
|
189 | - } |
|
190 | - |
|
191 | - |
|
192 | - /** |
|
193 | - * @param array $request_data |
|
194 | - * @param EE_Base_Class|null $log_object |
|
195 | - * @return array |
|
196 | - * @throws EE_Error |
|
197 | - * @throws ReflectionException |
|
198 | - */ |
|
199 | - private function processWaywardIPN( |
|
200 | - array $request_data, |
|
201 | - ?EE_Base_Class $log_object |
|
202 | - ): array { |
|
203 | - $payment = null; |
|
204 | - $payment_method = null; |
|
205 | - // that's actually pretty ok. The IPN just wasn't able |
|
206 | - // to identify which transaction or payment method this was for |
|
207 | - // give all active payment methods a chance to claim it |
|
208 | - $active_payment_methods = $this->payment_method_model->get_all_active(); |
|
209 | - foreach ($active_payment_methods as $payment_method) { |
|
210 | - try { |
|
211 | - $pm_type = $payment_method->type_obj(); |
|
212 | - $payment = $pm_type instanceof EE_PMT_Base ? $pm_type->handle_unclaimed_ipn($request_data) : $payment; |
|
213 | - $this->logIpnData(['IPN data' => $request_data], $payment); |
|
214 | - break; |
|
215 | - } catch (IpnException $exception) { |
|
216 | - $payment = $this->logIpnException($exception, $log_object); |
|
217 | - } catch (EE_Error $e) { |
|
218 | - // that's fine- it apparently couldn't handle the IPN |
|
219 | - } |
|
220 | - } |
|
221 | - return [$payment, $payment_method]; |
|
222 | - } |
|
223 | - |
|
224 | - |
|
225 | - /** |
|
226 | - * @param EE_Payment|null $payment |
|
227 | - * @param array $request_data |
|
228 | - * @param bool $update_txn |
|
229 | - * @param bool $separate_IPN_request |
|
230 | - * @param EE_Transaction|int|null $transaction optional (model object or a transaction's id) |
|
231 | - * @param EE_Payment_Method|int|string|null $payment_method optional (model object or a slug or id of PM) |
|
232 | - * @return EE_Payment|null |
|
233 | - * @throws EE_Error |
|
234 | - * @throws ReflectionException |
|
235 | - */ |
|
236 | - private function postIpnProcessing( |
|
237 | - ?EE_Payment $payment, |
|
238 | - array $request_data, |
|
239 | - bool $update_txn, |
|
240 | - bool $separate_IPN_request, |
|
241 | - $transaction = null, |
|
242 | - $payment_method = null |
|
243 | - ): ?EE_Payment { |
|
244 | - if ($payment instanceof EE_Payment) { |
|
245 | - $payment->save(); |
|
246 | - $this->payment_processor->updateTransactionBasedOnPayment( |
|
247 | - $transaction, |
|
248 | - $payment, |
|
249 | - $update_txn, |
|
250 | - $separate_IPN_request |
|
251 | - ); |
|
252 | - } else { |
|
253 | - // we couldn't find the payment for this IPN... let's try and log at least SOMETHING |
|
254 | - if ($payment_method) { |
|
255 | - $this->logIpnData(['IPN data' => $request_data], $payment_method); |
|
256 | - } elseif ($transaction) { |
|
257 | - $this->logIpnData(['IPN data' => $request_data], $transaction); |
|
258 | - } |
|
259 | - } |
|
260 | - return $payment; |
|
261 | - } |
|
262 | - |
|
263 | - |
|
264 | - /** |
|
265 | - * @param array $request_data |
|
266 | - * @param EE_Transaction|int|null $transaction optional (model object or a transaction's id) |
|
267 | - * @param EE_Payment_Method|int|string|null $payment_method optional (model object or a slug or id of PM) |
|
268 | - * @return array |
|
269 | - * @throws EE_Error |
|
270 | - * @throws ReflectionException |
|
271 | - */ |
|
272 | - private function logIPN( |
|
273 | - array $request_data, |
|
274 | - $transaction = null, |
|
275 | - $payment_method = null |
|
276 | - ): array { |
|
277 | - $log_object = null; |
|
278 | - if ($transaction instanceof EE_Transaction) { |
|
279 | - $log_object = $transaction; |
|
280 | - if ($payment_method instanceof EE_Payment_Method) { |
|
281 | - $log_object = EEM_Payment::instance()->get_one( |
|
282 | - [ |
|
283 | - ['TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()], |
|
284 | - 'order_by' => ['PAY_timestamp' => 'desc'], |
|
285 | - ] |
|
286 | - ); |
|
287 | - } |
|
288 | - } |
|
289 | - $log = $this->logIpnData(['IPN data received' => $request_data], $log_object); |
|
290 | - return [ |
|
291 | - 'log' => $log, |
|
292 | - 'object' => $log_object, |
|
293 | - ]; |
|
294 | - } |
|
295 | - |
|
296 | - |
|
297 | - /** |
|
298 | - * @param array $data |
|
299 | - * @param EE_Base_Class|null $log_object |
|
300 | - * @return EE_Change_Log |
|
301 | - * @throws EE_Error |
|
302 | - * @throws ReflectionException |
|
303 | - */ |
|
304 | - private function logIpnData(array $data, ?EE_Base_Class $log_object): EE_Change_Log |
|
305 | - { |
|
306 | - return EEM_Change_Log::instance()->log(EEM_Change_Log::type_gateway, $data, $log_object); |
|
307 | - } |
|
308 | - |
|
309 | - |
|
310 | - /** |
|
311 | - * @param IpnException $exception |
|
312 | - * @param EE_Base_Class|null $log_object |
|
313 | - * @return EE_Payment|null |
|
314 | - * @throws EE_Error |
|
315 | - * @throws ReflectionException |
|
316 | - */ |
|
317 | - private function logIpnException(IpnException $exception, ?EE_Base_Class $log_object): ?EE_Payment |
|
318 | - { |
|
319 | - $this->logIpnData( |
|
320 | - [ |
|
321 | - 'message' => 'IPN Exception: ' . $exception->getMessage(), |
|
322 | - 'current_url' => EEH_URL::current_url(), |
|
323 | - 'payment' => $exception->getPaymentProperties(), |
|
324 | - 'IPN_data' => $exception->getIpnData(), |
|
325 | - ], |
|
326 | - $log_object |
|
327 | - ); |
|
328 | - return $exception->getPayment(); |
|
329 | - } |
|
330 | - |
|
331 | - |
|
332 | - /** |
|
333 | - * @return void |
|
334 | - * @throws EE_Error |
|
335 | - */ |
|
336 | - private function invalidPaymentError() |
|
337 | - { |
|
338 | - EE_Error::add_error( |
|
339 | - sprintf( |
|
340 | - esc_html__( |
|
341 | - 'A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.', |
|
342 | - 'event_espresso' |
|
343 | - ), |
|
344 | - '<br/>', |
|
345 | - $this->organization->get_pretty('email') |
|
346 | - ), |
|
347 | - __FILE__, |
|
348 | - __FUNCTION__, |
|
349 | - __LINE__ |
|
350 | - ); |
|
351 | - } |
|
352 | - |
|
353 | - |
|
354 | - /** |
|
355 | - * @param Exception $exception |
|
356 | - * @param $transaction |
|
357 | - * @param $request_data |
|
358 | - * @return mixed |
|
359 | - * @throws Exception |
|
360 | - */ |
|
361 | - private function invalidIpnException(Exception $exception, $transaction, $request_data) |
|
362 | - { |
|
363 | - do_action( |
|
364 | - 'AHEE__log', |
|
365 | - __FILE__, |
|
366 | - __FUNCTION__, |
|
367 | - sprintf( |
|
368 | - esc_html__( |
|
369 | - 'Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"', |
|
370 | - 'event_espresso' |
|
371 | - ), |
|
372 | - print_r($transaction, true), |
|
373 | - print_r($request_data, true), |
|
374 | - $exception->getMessage() |
|
375 | - ) |
|
376 | - ); |
|
377 | - throw $exception; |
|
378 | - } |
|
379 | - |
|
380 | - |
|
381 | - /** |
|
382 | - * Removes any non-printable illegal characters from the input, |
|
383 | - * which might cause a raucous when trying to insert into the database |
|
384 | - * |
|
385 | - * @param array $request_data |
|
386 | - * @return array |
|
387 | - */ |
|
388 | - private function removeUnusableCharactersFromArray(array $request_data): array |
|
389 | - { |
|
390 | - $return_data = []; |
|
391 | - foreach ($request_data as $key => $value) { |
|
392 | - $return_data[ $this->removeUnusableCharacters($key) ] = $this->removeUnusableCharacters($value); |
|
393 | - } |
|
394 | - return $return_data; |
|
395 | - } |
|
396 | - |
|
397 | - |
|
398 | - /** |
|
399 | - * Removes any non-printable illegal characters from the input, |
|
400 | - * which might cause a raucous when trying to insert into the database |
|
401 | - * |
|
402 | - * @param string $request_data |
|
403 | - * @return string |
|
404 | - */ |
|
405 | - private function removeUnusableCharacters(string $request_data): string |
|
406 | - { |
|
407 | - return preg_replace('/[^[:print:]]/', '', $request_data); |
|
408 | - } |
|
35 | + private EEM_Payment_Method $payment_method_model; |
|
36 | + |
|
37 | + private EEM_Transaction $transaction_model; |
|
38 | + |
|
39 | + private EE_Core_Config $core_config; |
|
40 | + |
|
41 | + private EE_Organization_Config $organization; |
|
42 | + |
|
43 | + private PaymentProcessor $payment_processor; |
|
44 | + |
|
45 | + |
|
46 | + /** |
|
47 | + * @param EEM_Payment_Method $payment_method_model |
|
48 | + * @param EEM_Transaction $transaction_model |
|
49 | + * @param EE_Core_Config $core_config |
|
50 | + * @param EE_Organization_Config $organization |
|
51 | + * @param PaymentProcessor $payment_processor |
|
52 | + */ |
|
53 | + public function __construct( |
|
54 | + EEM_Payment_Method $payment_method_model, |
|
55 | + EEM_Transaction $transaction_model, |
|
56 | + EE_Core_Config $core_config, |
|
57 | + EE_Organization_Config $organization, |
|
58 | + PaymentProcessor $payment_processor |
|
59 | + ) { |
|
60 | + $this->payment_method_model = $payment_method_model; |
|
61 | + $this->transaction_model = $transaction_model; |
|
62 | + $this->core_config = $core_config; |
|
63 | + $this->organization = $organization; |
|
64 | + $this->payment_processor = $payment_processor; |
|
65 | + } |
|
66 | + |
|
67 | + |
|
68 | + /** |
|
69 | + * @param EE_Transaction $transaction |
|
70 | + * @param EE_Payment_Method $payment_method |
|
71 | + * @return string |
|
72 | + * @throws EE_Error |
|
73 | + * @throws ReflectionException |
|
74 | + */ |
|
75 | + public function getIpnUrlForPaymentMethod(EE_Transaction $transaction, EE_Payment_Method $payment_method): string |
|
76 | + { |
|
77 | + $primary_reg = $transaction->primary_registration(); |
|
78 | + if (! $primary_reg instanceof EE_Registration) { |
|
79 | + throw new EE_Error( |
|
80 | + sprintf( |
|
81 | + esc_html__( |
|
82 | + 'Cannot get IPN URL for transaction with ID %d because it has no primary registration', |
|
83 | + 'event_espresso' |
|
84 | + ), |
|
85 | + $transaction->ID() |
|
86 | + ) |
|
87 | + ); |
|
88 | + } |
|
89 | + $payment_method = $this->payment_method_model->ensure_is_obj( |
|
90 | + $payment_method, |
|
91 | + true |
|
92 | + ); |
|
93 | + return add_query_arg( |
|
94 | + [ |
|
95 | + 'e_reg_url_link' => $primary_reg->reg_url_link(), |
|
96 | + 'ee_payment_method' => $payment_method->slug(), |
|
97 | + ], |
|
98 | + $this->core_config->txn_page_url() |
|
99 | + ); |
|
100 | + } |
|
101 | + |
|
102 | + |
|
103 | + /** |
|
104 | + * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so |
|
105 | + * we can easily find what registration the IPN is for and what payment method. |
|
106 | + * However, if not, we'll give all payment methods a chance to claim it and process it. |
|
107 | + * If a payment is found for the IPN info, it is saved. |
|
108 | + * |
|
109 | + * @param array $request_data form post data |
|
110 | + * @param EE_Transaction|int|null $transaction optional (or a transaction's id) |
|
111 | + * @param EE_Payment_Method|int|string|null $payment_method optional (or a slug or id of PM) |
|
112 | + * @param bool $update_txn whether to call |
|
113 | + * EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() |
|
114 | + * @param bool $separate_IPN_request whether the IPN uses a separate request (true, |
|
115 | + * like PayPal) or is processed manually (false, |
|
116 | + * like Authorize.net) |
|
117 | + * @return EE_Payment |
|
118 | + * @throws EE_Error |
|
119 | + * @throws ReflectionException |
|
120 | + */ |
|
121 | + public function processIPN( |
|
122 | + array $request_data, |
|
123 | + $transaction = null, |
|
124 | + $payment_method = null, |
|
125 | + bool $update_txn = true, |
|
126 | + bool $separate_IPN_request = true |
|
127 | + ): EE_Payment { |
|
128 | + $request_data = $this->removeUnusableCharactersFromArray($request_data); |
|
129 | + EE_Processor_Base::set_IPN($separate_IPN_request); |
|
130 | + ['log' => $log, 'object' => $log_object] = $this->logIPN($request_data, $transaction, $payment_method); |
|
131 | + |
|
132 | + try { |
|
133 | + if ($transaction && $payment_method) { |
|
134 | + $payment = $this->processStandardIPN($request_data, $log, $log_object, $transaction, $payment_method); |
|
135 | + } else { |
|
136 | + ['payment' => $payment, 'payment_method' => $payment_method] = $this->processWaywardIPN( |
|
137 | + $request_data, |
|
138 | + $log_object |
|
139 | + ); |
|
140 | + } |
|
141 | + return $this->postIpnProcessing( |
|
142 | + $payment, |
|
143 | + $request_data, |
|
144 | + $update_txn, |
|
145 | + $separate_IPN_request, |
|
146 | + $transaction, |
|
147 | + $payment_method |
|
148 | + ); |
|
149 | + } catch (EE_Error $exception) { |
|
150 | + $this->invalidIpnException($exception, $transaction, $request_data); |
|
151 | + } |
|
152 | + } |
|
153 | + |
|
154 | + |
|
155 | + /** |
|
156 | + * @param array $request_data |
|
157 | + * @param EE_Change_Log $log |
|
158 | + * @param EE_Base_Class|null $log_object |
|
159 | + * @param EE_Transaction|int|null $transaction optional (model object or a transaction's id) |
|
160 | + * @param EE_Payment_Method|int|string|null $payment_method optional (model object or a slug or id of PM) |
|
161 | + * @return EE_Payment|null |
|
162 | + * @throws EE_Error |
|
163 | + * @throws ReflectionException |
|
164 | + */ |
|
165 | + private function processStandardIPN( |
|
166 | + array $request_data, |
|
167 | + EE_Change_Log $log, |
|
168 | + ?EE_Base_Class $log_object, |
|
169 | + $transaction = null, |
|
170 | + $payment_method = null |
|
171 | + ): ?EE_Payment { |
|
172 | + /** @type EE_Transaction $transaction */ |
|
173 | + $transaction = $this->transaction_model->ensure_is_obj($transaction); |
|
174 | + /** @type EE_Payment_Method $payment_method */ |
|
175 | + $payment_method = $this->payment_method_model->ensure_is_obj($payment_method); |
|
176 | + if (! $payment_method->type_obj() instanceof EE_PMT_Base) { |
|
177 | + // not a payment |
|
178 | + $this->invalidPaymentError(); |
|
179 | + return null; |
|
180 | + } |
|
181 | + try { |
|
182 | + $payment = $payment_method->type_obj()->handle_ipn($request_data, $transaction); |
|
183 | + $log->set_object($payment); |
|
184 | + return $payment; |
|
185 | + } catch (IpnException $exception) { |
|
186 | + $payment = $this->logIpnException($exception, $log_object); |
|
187 | + } |
|
188 | + return $payment; |
|
189 | + } |
|
190 | + |
|
191 | + |
|
192 | + /** |
|
193 | + * @param array $request_data |
|
194 | + * @param EE_Base_Class|null $log_object |
|
195 | + * @return array |
|
196 | + * @throws EE_Error |
|
197 | + * @throws ReflectionException |
|
198 | + */ |
|
199 | + private function processWaywardIPN( |
|
200 | + array $request_data, |
|
201 | + ?EE_Base_Class $log_object |
|
202 | + ): array { |
|
203 | + $payment = null; |
|
204 | + $payment_method = null; |
|
205 | + // that's actually pretty ok. The IPN just wasn't able |
|
206 | + // to identify which transaction or payment method this was for |
|
207 | + // give all active payment methods a chance to claim it |
|
208 | + $active_payment_methods = $this->payment_method_model->get_all_active(); |
|
209 | + foreach ($active_payment_methods as $payment_method) { |
|
210 | + try { |
|
211 | + $pm_type = $payment_method->type_obj(); |
|
212 | + $payment = $pm_type instanceof EE_PMT_Base ? $pm_type->handle_unclaimed_ipn($request_data) : $payment; |
|
213 | + $this->logIpnData(['IPN data' => $request_data], $payment); |
|
214 | + break; |
|
215 | + } catch (IpnException $exception) { |
|
216 | + $payment = $this->logIpnException($exception, $log_object); |
|
217 | + } catch (EE_Error $e) { |
|
218 | + // that's fine- it apparently couldn't handle the IPN |
|
219 | + } |
|
220 | + } |
|
221 | + return [$payment, $payment_method]; |
|
222 | + } |
|
223 | + |
|
224 | + |
|
225 | + /** |
|
226 | + * @param EE_Payment|null $payment |
|
227 | + * @param array $request_data |
|
228 | + * @param bool $update_txn |
|
229 | + * @param bool $separate_IPN_request |
|
230 | + * @param EE_Transaction|int|null $transaction optional (model object or a transaction's id) |
|
231 | + * @param EE_Payment_Method|int|string|null $payment_method optional (model object or a slug or id of PM) |
|
232 | + * @return EE_Payment|null |
|
233 | + * @throws EE_Error |
|
234 | + * @throws ReflectionException |
|
235 | + */ |
|
236 | + private function postIpnProcessing( |
|
237 | + ?EE_Payment $payment, |
|
238 | + array $request_data, |
|
239 | + bool $update_txn, |
|
240 | + bool $separate_IPN_request, |
|
241 | + $transaction = null, |
|
242 | + $payment_method = null |
|
243 | + ): ?EE_Payment { |
|
244 | + if ($payment instanceof EE_Payment) { |
|
245 | + $payment->save(); |
|
246 | + $this->payment_processor->updateTransactionBasedOnPayment( |
|
247 | + $transaction, |
|
248 | + $payment, |
|
249 | + $update_txn, |
|
250 | + $separate_IPN_request |
|
251 | + ); |
|
252 | + } else { |
|
253 | + // we couldn't find the payment for this IPN... let's try and log at least SOMETHING |
|
254 | + if ($payment_method) { |
|
255 | + $this->logIpnData(['IPN data' => $request_data], $payment_method); |
|
256 | + } elseif ($transaction) { |
|
257 | + $this->logIpnData(['IPN data' => $request_data], $transaction); |
|
258 | + } |
|
259 | + } |
|
260 | + return $payment; |
|
261 | + } |
|
262 | + |
|
263 | + |
|
264 | + /** |
|
265 | + * @param array $request_data |
|
266 | + * @param EE_Transaction|int|null $transaction optional (model object or a transaction's id) |
|
267 | + * @param EE_Payment_Method|int|string|null $payment_method optional (model object or a slug or id of PM) |
|
268 | + * @return array |
|
269 | + * @throws EE_Error |
|
270 | + * @throws ReflectionException |
|
271 | + */ |
|
272 | + private function logIPN( |
|
273 | + array $request_data, |
|
274 | + $transaction = null, |
|
275 | + $payment_method = null |
|
276 | + ): array { |
|
277 | + $log_object = null; |
|
278 | + if ($transaction instanceof EE_Transaction) { |
|
279 | + $log_object = $transaction; |
|
280 | + if ($payment_method instanceof EE_Payment_Method) { |
|
281 | + $log_object = EEM_Payment::instance()->get_one( |
|
282 | + [ |
|
283 | + ['TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()], |
|
284 | + 'order_by' => ['PAY_timestamp' => 'desc'], |
|
285 | + ] |
|
286 | + ); |
|
287 | + } |
|
288 | + } |
|
289 | + $log = $this->logIpnData(['IPN data received' => $request_data], $log_object); |
|
290 | + return [ |
|
291 | + 'log' => $log, |
|
292 | + 'object' => $log_object, |
|
293 | + ]; |
|
294 | + } |
|
295 | + |
|
296 | + |
|
297 | + /** |
|
298 | + * @param array $data |
|
299 | + * @param EE_Base_Class|null $log_object |
|
300 | + * @return EE_Change_Log |
|
301 | + * @throws EE_Error |
|
302 | + * @throws ReflectionException |
|
303 | + */ |
|
304 | + private function logIpnData(array $data, ?EE_Base_Class $log_object): EE_Change_Log |
|
305 | + { |
|
306 | + return EEM_Change_Log::instance()->log(EEM_Change_Log::type_gateway, $data, $log_object); |
|
307 | + } |
|
308 | + |
|
309 | + |
|
310 | + /** |
|
311 | + * @param IpnException $exception |
|
312 | + * @param EE_Base_Class|null $log_object |
|
313 | + * @return EE_Payment|null |
|
314 | + * @throws EE_Error |
|
315 | + * @throws ReflectionException |
|
316 | + */ |
|
317 | + private function logIpnException(IpnException $exception, ?EE_Base_Class $log_object): ?EE_Payment |
|
318 | + { |
|
319 | + $this->logIpnData( |
|
320 | + [ |
|
321 | + 'message' => 'IPN Exception: ' . $exception->getMessage(), |
|
322 | + 'current_url' => EEH_URL::current_url(), |
|
323 | + 'payment' => $exception->getPaymentProperties(), |
|
324 | + 'IPN_data' => $exception->getIpnData(), |
|
325 | + ], |
|
326 | + $log_object |
|
327 | + ); |
|
328 | + return $exception->getPayment(); |
|
329 | + } |
|
330 | + |
|
331 | + |
|
332 | + /** |
|
333 | + * @return void |
|
334 | + * @throws EE_Error |
|
335 | + */ |
|
336 | + private function invalidPaymentError() |
|
337 | + { |
|
338 | + EE_Error::add_error( |
|
339 | + sprintf( |
|
340 | + esc_html__( |
|
341 | + 'A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.', |
|
342 | + 'event_espresso' |
|
343 | + ), |
|
344 | + '<br/>', |
|
345 | + $this->organization->get_pretty('email') |
|
346 | + ), |
|
347 | + __FILE__, |
|
348 | + __FUNCTION__, |
|
349 | + __LINE__ |
|
350 | + ); |
|
351 | + } |
|
352 | + |
|
353 | + |
|
354 | + /** |
|
355 | + * @param Exception $exception |
|
356 | + * @param $transaction |
|
357 | + * @param $request_data |
|
358 | + * @return mixed |
|
359 | + * @throws Exception |
|
360 | + */ |
|
361 | + private function invalidIpnException(Exception $exception, $transaction, $request_data) |
|
362 | + { |
|
363 | + do_action( |
|
364 | + 'AHEE__log', |
|
365 | + __FILE__, |
|
366 | + __FUNCTION__, |
|
367 | + sprintf( |
|
368 | + esc_html__( |
|
369 | + 'Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"', |
|
370 | + 'event_espresso' |
|
371 | + ), |
|
372 | + print_r($transaction, true), |
|
373 | + print_r($request_data, true), |
|
374 | + $exception->getMessage() |
|
375 | + ) |
|
376 | + ); |
|
377 | + throw $exception; |
|
378 | + } |
|
379 | + |
|
380 | + |
|
381 | + /** |
|
382 | + * Removes any non-printable illegal characters from the input, |
|
383 | + * which might cause a raucous when trying to insert into the database |
|
384 | + * |
|
385 | + * @param array $request_data |
|
386 | + * @return array |
|
387 | + */ |
|
388 | + private function removeUnusableCharactersFromArray(array $request_data): array |
|
389 | + { |
|
390 | + $return_data = []; |
|
391 | + foreach ($request_data as $key => $value) { |
|
392 | + $return_data[ $this->removeUnusableCharacters($key) ] = $this->removeUnusableCharacters($value); |
|
393 | + } |
|
394 | + return $return_data; |
|
395 | + } |
|
396 | + |
|
397 | + |
|
398 | + /** |
|
399 | + * Removes any non-printable illegal characters from the input, |
|
400 | + * which might cause a raucous when trying to insert into the database |
|
401 | + * |
|
402 | + * @param string $request_data |
|
403 | + * @return string |
|
404 | + */ |
|
405 | + private function removeUnusableCharacters(string $request_data): string |
|
406 | + { |
|
407 | + return preg_replace('/[^[:print:]]/', '', $request_data); |
|
408 | + } |
|
409 | 409 | |
410 | 410 | } |
@@ -75,7 +75,7 @@ discard block |
||
75 | 75 | public function getIpnUrlForPaymentMethod(EE_Transaction $transaction, EE_Payment_Method $payment_method): string |
76 | 76 | { |
77 | 77 | $primary_reg = $transaction->primary_registration(); |
78 | - if (! $primary_reg instanceof EE_Registration) { |
|
78 | + if ( ! $primary_reg instanceof EE_Registration) { |
|
79 | 79 | throw new EE_Error( |
80 | 80 | sprintf( |
81 | 81 | esc_html__( |
@@ -173,7 +173,7 @@ discard block |
||
173 | 173 | $transaction = $this->transaction_model->ensure_is_obj($transaction); |
174 | 174 | /** @type EE_Payment_Method $payment_method */ |
175 | 175 | $payment_method = $this->payment_method_model->ensure_is_obj($payment_method); |
176 | - if (! $payment_method->type_obj() instanceof EE_PMT_Base) { |
|
176 | + if ( ! $payment_method->type_obj() instanceof EE_PMT_Base) { |
|
177 | 177 | // not a payment |
178 | 178 | $this->invalidPaymentError(); |
179 | 179 | return null; |
@@ -318,7 +318,7 @@ discard block |
||
318 | 318 | { |
319 | 319 | $this->logIpnData( |
320 | 320 | [ |
321 | - 'message' => 'IPN Exception: ' . $exception->getMessage(), |
|
321 | + 'message' => 'IPN Exception: '.$exception->getMessage(), |
|
322 | 322 | 'current_url' => EEH_URL::current_url(), |
323 | 323 | 'payment' => $exception->getPaymentProperties(), |
324 | 324 | 'IPN_data' => $exception->getIpnData(), |
@@ -389,7 +389,7 @@ discard block |
||
389 | 389 | { |
390 | 390 | $return_data = []; |
391 | 391 | foreach ($request_data as $key => $value) { |
392 | - $return_data[ $this->removeUnusableCharacters($key) ] = $this->removeUnusableCharacters($value); |
|
392 | + $return_data[$this->removeUnusableCharacters($key)] = $this->removeUnusableCharacters($value); |
|
393 | 393 | } |
394 | 394 | return $return_data; |
395 | 395 | } |
@@ -31,316 +31,316 @@ |
||
31 | 31 | { |
32 | 32 | |
33 | 33 | |
34 | - private EEM_Payment_Method $payment_method_model; |
|
34 | + private EEM_Payment_Method $payment_method_model; |
|
35 | 35 | |
36 | - private EEM_Transaction $transaction_model; |
|
36 | + private EEM_Transaction $transaction_model; |
|
37 | 37 | |
38 | - private EE_Organization_Config $organization; |
|
38 | + private EE_Organization_Config $organization; |
|
39 | 39 | |
40 | - private FeatureFlags $feature; |
|
40 | + private FeatureFlags $feature; |
|
41 | 41 | |
42 | - private PaymentProcessorFees $payment_processor_fees; |
|
42 | + private PaymentProcessorFees $payment_processor_fees; |
|
43 | 43 | |
44 | - private PostPaymentProcessor $post_payment_processor; |
|
44 | + private PostPaymentProcessor $post_payment_processor; |
|
45 | 45 | |
46 | - private RegistrationPayments $registration_payments; |
|
46 | + private RegistrationPayments $registration_payments; |
|
47 | 47 | |
48 | 48 | |
49 | - /** |
|
50 | - * @param EEM_Payment_Method $payment_method_model |
|
51 | - * @param EEM_Transaction $transaction_model |
|
52 | - * @param EE_Organization_Config $organization |
|
53 | - * @param FeatureFlags $feature |
|
54 | - * @param PaymentProcessorFees $payment_processor_fees |
|
55 | - * @param PostPaymentProcessor $post_payment_processor |
|
56 | - * @param RegistrationPayments $registration_payments |
|
57 | - */ |
|
58 | - public function __construct( |
|
59 | - EEM_Payment_Method $payment_method_model, |
|
60 | - EEM_Transaction $transaction_model, |
|
61 | - EE_Organization_Config $organization, |
|
62 | - FeatureFlags $feature, |
|
63 | - PaymentProcessorFees $payment_processor_fees, |
|
64 | - PostPaymentProcessor $post_payment_processor, |
|
65 | - RegistrationPayments $registration_payments |
|
66 | - ) { |
|
67 | - $this->payment_method_model = $payment_method_model; |
|
68 | - $this->transaction_model = $transaction_model; |
|
69 | - $this->organization = $organization; |
|
70 | - $this->feature = $feature; |
|
71 | - $this->payment_processor_fees = $payment_processor_fees; |
|
72 | - $this->post_payment_processor = $post_payment_processor; |
|
73 | - $this->registration_payments = $registration_payments; |
|
74 | - } |
|
49 | + /** |
|
50 | + * @param EEM_Payment_Method $payment_method_model |
|
51 | + * @param EEM_Transaction $transaction_model |
|
52 | + * @param EE_Organization_Config $organization |
|
53 | + * @param FeatureFlags $feature |
|
54 | + * @param PaymentProcessorFees $payment_processor_fees |
|
55 | + * @param PostPaymentProcessor $post_payment_processor |
|
56 | + * @param RegistrationPayments $registration_payments |
|
57 | + */ |
|
58 | + public function __construct( |
|
59 | + EEM_Payment_Method $payment_method_model, |
|
60 | + EEM_Transaction $transaction_model, |
|
61 | + EE_Organization_Config $organization, |
|
62 | + FeatureFlags $feature, |
|
63 | + PaymentProcessorFees $payment_processor_fees, |
|
64 | + PostPaymentProcessor $post_payment_processor, |
|
65 | + RegistrationPayments $registration_payments |
|
66 | + ) { |
|
67 | + $this->payment_method_model = $payment_method_model; |
|
68 | + $this->transaction_model = $transaction_model; |
|
69 | + $this->organization = $organization; |
|
70 | + $this->feature = $feature; |
|
71 | + $this->payment_processor_fees = $payment_processor_fees; |
|
72 | + $this->post_payment_processor = $post_payment_processor; |
|
73 | + $this->registration_payments = $registration_payments; |
|
74 | + } |
|
75 | 75 | |
76 | 76 | |
77 | - /** |
|
78 | - * Using the selected gateway, processes the payment for that transaction, and updates the transaction |
|
79 | - * appropriately. Saves the payment that is generated |
|
80 | - * |
|
81 | - * @param EE_Payment_Method $payment_method |
|
82 | - * @param EE_Transaction $transaction |
|
83 | - * @param EE_Billing_Info_Form|null $billing_form (or probably null, if it's an offline or offsite payment |
|
84 | - * method). |
|
85 | - * Receive_form_submission() should have |
|
86 | - * already been called on the billing form |
|
87 | - * (ie, its inputs should have their normalized values set). |
|
88 | - * @param float $amount if only part of the transaction is to be paid for, how much. |
|
89 | - * Leave null if payment is for the full amount owing |
|
90 | - * @param bool $by_admin TRUE if payment is being attempted from the admin |
|
91 | - * @param bool $update_txn whether to call EE_Transaction_Processor |
|
92 | - * ::update_transaction_and_registrations_after_checkout_or_payment() |
|
93 | - * @param string $return_url string used mostly by offsite gateways to specify |
|
94 | - * where to go AFTER the offsite gateway |
|
95 | - * @param string $cancel_url URL to return to if off-site payments are cancelled |
|
96 | - * @param string $method like 'CART', indicates who the client who called this was |
|
97 | - * @return EE_Payment|null |
|
98 | - * @throws EE_Error |
|
99 | - * @throws Exception |
|
100 | - * @throws ReflectionException |
|
101 | - */ |
|
102 | - public function processPayment( |
|
103 | - EE_Payment_Method $payment_method, |
|
104 | - EE_Transaction $transaction, |
|
105 | - ?EE_Billing_Info_Form $billing_form = null, |
|
106 | - float $amount = 0.00, |
|
107 | - bool $by_admin = false, |
|
108 | - bool $update_txn = true, |
|
109 | - string $return_url = '', |
|
110 | - string $cancel_url = '', |
|
111 | - string $method = 'CART' |
|
112 | - ): ?EE_Payment { |
|
113 | - $this->validatePaymentAmount($transaction, $amount); |
|
114 | - // verify payment method |
|
115 | - $payment_method = $this->payment_method_model->ensure_is_obj( |
|
116 | - $payment_method, |
|
117 | - true |
|
118 | - ); |
|
119 | - // verify transaction |
|
120 | - $this->transaction_model->ensure_is_obj($transaction); |
|
121 | - $transaction->set_payment_method_ID($payment_method->ID()); |
|
122 | - // make sure we don't overcharge |
|
123 | - $amount = min($amount, $transaction->remaining()); |
|
124 | - // TODO: add some extra logic to PaymentProcessorFees to designate where/when partner fees are applied based on the PM being used. |
|
125 | - // if ( |
|
126 | - // $this->feature->allowed(FeatureFlag::USE_PAYMENT_PROCESSOR_FEES) |
|
127 | - // && apply_filters( |
|
128 | - // 'FHEE__EE_Payment_Processor__apply_gateway_partner_fees', |
|
129 | - // true, |
|
130 | - // $transaction, |
|
131 | - // $payment_method, |
|
132 | - // $amount |
|
133 | - // ) |
|
134 | - // ) { |
|
135 | - // $amount = $this->payment_processor_fees->applyGatewayPartnerFees( |
|
136 | - // $transaction, |
|
137 | - // $payment_method->name(), |
|
138 | - // $amount |
|
139 | - // ); |
|
140 | - // } |
|
141 | - // verify payment method type |
|
142 | - if ($payment_method->type_obj() instanceof EE_PMT_Base) { |
|
143 | - $payment = $payment_method->type_obj()->process_payment( |
|
144 | - $transaction, |
|
145 | - $amount, |
|
146 | - $billing_form, |
|
147 | - $return_url, |
|
148 | - add_query_arg(['ee_cancel_payment' => true], $cancel_url), |
|
149 | - $method, |
|
150 | - $by_admin |
|
151 | - ); |
|
152 | - // check if payment method uses an off-site gateway |
|
153 | - if ($payment_method->type_obj()->payment_occurs() !== EE_PMT_Base::offsite) { |
|
154 | - // don't process payments for off-site gateways yet because no payment has occurred yet |
|
155 | - $this->updateTransactionBasedOnPayment($transaction, $payment, $update_txn); |
|
156 | - } |
|
157 | - return $payment; |
|
158 | - } |
|
159 | - EE_Error::add_error( |
|
160 | - sprintf( |
|
161 | - esc_html__( |
|
162 | - 'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.', |
|
163 | - 'event_espresso' |
|
164 | - ), |
|
165 | - '<br/>', |
|
166 | - $this->organization->get_pretty('email') |
|
167 | - ), |
|
168 | - __FILE__, |
|
169 | - __FUNCTION__, |
|
170 | - __LINE__ |
|
171 | - ); |
|
172 | - return null; |
|
173 | - } |
|
77 | + /** |
|
78 | + * Using the selected gateway, processes the payment for that transaction, and updates the transaction |
|
79 | + * appropriately. Saves the payment that is generated |
|
80 | + * |
|
81 | + * @param EE_Payment_Method $payment_method |
|
82 | + * @param EE_Transaction $transaction |
|
83 | + * @param EE_Billing_Info_Form|null $billing_form (or probably null, if it's an offline or offsite payment |
|
84 | + * method). |
|
85 | + * Receive_form_submission() should have |
|
86 | + * already been called on the billing form |
|
87 | + * (ie, its inputs should have their normalized values set). |
|
88 | + * @param float $amount if only part of the transaction is to be paid for, how much. |
|
89 | + * Leave null if payment is for the full amount owing |
|
90 | + * @param bool $by_admin TRUE if payment is being attempted from the admin |
|
91 | + * @param bool $update_txn whether to call EE_Transaction_Processor |
|
92 | + * ::update_transaction_and_registrations_after_checkout_or_payment() |
|
93 | + * @param string $return_url string used mostly by offsite gateways to specify |
|
94 | + * where to go AFTER the offsite gateway |
|
95 | + * @param string $cancel_url URL to return to if off-site payments are cancelled |
|
96 | + * @param string $method like 'CART', indicates who the client who called this was |
|
97 | + * @return EE_Payment|null |
|
98 | + * @throws EE_Error |
|
99 | + * @throws Exception |
|
100 | + * @throws ReflectionException |
|
101 | + */ |
|
102 | + public function processPayment( |
|
103 | + EE_Payment_Method $payment_method, |
|
104 | + EE_Transaction $transaction, |
|
105 | + ?EE_Billing_Info_Form $billing_form = null, |
|
106 | + float $amount = 0.00, |
|
107 | + bool $by_admin = false, |
|
108 | + bool $update_txn = true, |
|
109 | + string $return_url = '', |
|
110 | + string $cancel_url = '', |
|
111 | + string $method = 'CART' |
|
112 | + ): ?EE_Payment { |
|
113 | + $this->validatePaymentAmount($transaction, $amount); |
|
114 | + // verify payment method |
|
115 | + $payment_method = $this->payment_method_model->ensure_is_obj( |
|
116 | + $payment_method, |
|
117 | + true |
|
118 | + ); |
|
119 | + // verify transaction |
|
120 | + $this->transaction_model->ensure_is_obj($transaction); |
|
121 | + $transaction->set_payment_method_ID($payment_method->ID()); |
|
122 | + // make sure we don't overcharge |
|
123 | + $amount = min($amount, $transaction->remaining()); |
|
124 | + // TODO: add some extra logic to PaymentProcessorFees to designate where/when partner fees are applied based on the PM being used. |
|
125 | + // if ( |
|
126 | + // $this->feature->allowed(FeatureFlag::USE_PAYMENT_PROCESSOR_FEES) |
|
127 | + // && apply_filters( |
|
128 | + // 'FHEE__EE_Payment_Processor__apply_gateway_partner_fees', |
|
129 | + // true, |
|
130 | + // $transaction, |
|
131 | + // $payment_method, |
|
132 | + // $amount |
|
133 | + // ) |
|
134 | + // ) { |
|
135 | + // $amount = $this->payment_processor_fees->applyGatewayPartnerFees( |
|
136 | + // $transaction, |
|
137 | + // $payment_method->name(), |
|
138 | + // $amount |
|
139 | + // ); |
|
140 | + // } |
|
141 | + // verify payment method type |
|
142 | + if ($payment_method->type_obj() instanceof EE_PMT_Base) { |
|
143 | + $payment = $payment_method->type_obj()->process_payment( |
|
144 | + $transaction, |
|
145 | + $amount, |
|
146 | + $billing_form, |
|
147 | + $return_url, |
|
148 | + add_query_arg(['ee_cancel_payment' => true], $cancel_url), |
|
149 | + $method, |
|
150 | + $by_admin |
|
151 | + ); |
|
152 | + // check if payment method uses an off-site gateway |
|
153 | + if ($payment_method->type_obj()->payment_occurs() !== EE_PMT_Base::offsite) { |
|
154 | + // don't process payments for off-site gateways yet because no payment has occurred yet |
|
155 | + $this->updateTransactionBasedOnPayment($transaction, $payment, $update_txn); |
|
156 | + } |
|
157 | + return $payment; |
|
158 | + } |
|
159 | + EE_Error::add_error( |
|
160 | + sprintf( |
|
161 | + esc_html__( |
|
162 | + 'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.', |
|
163 | + 'event_espresso' |
|
164 | + ), |
|
165 | + '<br/>', |
|
166 | + $this->organization->get_pretty('email') |
|
167 | + ), |
|
168 | + __FILE__, |
|
169 | + __FUNCTION__, |
|
170 | + __LINE__ |
|
171 | + ); |
|
172 | + return null; |
|
173 | + } |
|
174 | 174 | |
175 | 175 | |
176 | - /** |
|
177 | - * Processes a direct refund request, saves the payment, and updates the transaction appropriately. |
|
178 | - * |
|
179 | - * @param EE_Payment_Method|null $payment_method |
|
180 | - * @param EE_Payment $payment_to_refund |
|
181 | - * @param array $refund_info |
|
182 | - * @return EE_Payment |
|
183 | - * @throws EE_Error |
|
184 | - * @throws ReflectionException |
|
185 | - */ |
|
186 | - public function processRefund( |
|
187 | - ?EE_Payment_Method $payment_method, |
|
188 | - EE_Payment $payment_to_refund, |
|
189 | - array $refund_info = [] |
|
190 | - ): EE_Payment { |
|
191 | - if ($payment_method instanceof EE_Payment_Method && $payment_method->type_obj()->supports_sending_refunds()) { |
|
192 | - $payment_method->type_obj()->process_refund($payment_to_refund, $refund_info); |
|
193 | - $this->updateTransactionBasedOnPayment( |
|
194 | - $payment_to_refund->transaction(), |
|
195 | - $payment_to_refund |
|
196 | - ); |
|
197 | - } |
|
198 | - return $payment_to_refund; |
|
199 | - } |
|
176 | + /** |
|
177 | + * Processes a direct refund request, saves the payment, and updates the transaction appropriately. |
|
178 | + * |
|
179 | + * @param EE_Payment_Method|null $payment_method |
|
180 | + * @param EE_Payment $payment_to_refund |
|
181 | + * @param array $refund_info |
|
182 | + * @return EE_Payment |
|
183 | + * @throws EE_Error |
|
184 | + * @throws ReflectionException |
|
185 | + */ |
|
186 | + public function processRefund( |
|
187 | + ?EE_Payment_Method $payment_method, |
|
188 | + EE_Payment $payment_to_refund, |
|
189 | + array $refund_info = [] |
|
190 | + ): EE_Payment { |
|
191 | + if ($payment_method instanceof EE_Payment_Method && $payment_method->type_obj()->supports_sending_refunds()) { |
|
192 | + $payment_method->type_obj()->process_refund($payment_to_refund, $refund_info); |
|
193 | + $this->updateTransactionBasedOnPayment( |
|
194 | + $payment_to_refund->transaction(), |
|
195 | + $payment_to_refund |
|
196 | + ); |
|
197 | + } |
|
198 | + return $payment_to_refund; |
|
199 | + } |
|
200 | 200 | |
201 | 201 | |
202 | - /** |
|
203 | - * This should be called each time there may have been an update to a |
|
204 | - * payment on a transaction (ie, we asked for a payment to process a |
|
205 | - * payment for a transaction, or we told a payment method about an IPN, or |
|
206 | - * we told a payment method to |
|
207 | - * "finalize_payment_for" (a transaction), or we told a payment method to |
|
208 | - * process a refund. This should handle firing the correct hooks to |
|
209 | - * indicate |
|
210 | - * what exactly happened and updating the transaction appropriately). This |
|
211 | - * could be integrated directly into EE_Transaction upon save, but we want |
|
212 | - * this logic to be separate from 'normal' plain-jane saving and updating |
|
213 | - * of transactions and payments, and to be tied to payment processing. |
|
214 | - * Note: this method DOES NOT save the payment passed into it. It is the responsibility |
|
215 | - * of previous code to decide whether to save (because the payment passed into |
|
216 | - * this method might be a temporary, never-to-be-saved payment from an offline gateway, |
|
217 | - * in which case we only want that payment object for some temporary usage during this request, |
|
218 | - * but we don't want it to be saved). |
|
219 | - * |
|
220 | - * @param EE_Transaction $transaction |
|
221 | - * @param EE_Payment|null $payment |
|
222 | - * @param bool $update_txn whether to call EE_Transaction_Processor:: |
|
223 | - * update_transaction_and_registrations_after_checkout_or_payment() |
|
224 | - * (you can save 1 DB query if you know you're going |
|
225 | - * to save it later instead) |
|
226 | - * @param bool $IPN |
|
227 | - * if processing IPNs or other similar payment |
|
228 | - * related activities that occur in alternate |
|
229 | - * requests than the main one that is processing the |
|
230 | - * TXN, then set this to true to check whether the |
|
231 | - * TXN is locked before updating |
|
232 | - * @throws EE_Error |
|
233 | - * @throws ReflectionException |
|
234 | - */ |
|
235 | - public function updateTransactionBasedOnPayment( |
|
236 | - EE_Transaction $transaction, |
|
237 | - ?EE_Payment $payment, |
|
238 | - bool $update_txn = true, |
|
239 | - bool $IPN = false |
|
240 | - ) { |
|
241 | - $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful'; |
|
242 | - /** @type EE_Transaction $transaction */ |
|
243 | - $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction); |
|
244 | - // can we freely update the TXN at this moment? |
|
245 | - if ($IPN && $transaction->is_locked()) { |
|
246 | - // don't update the transaction at this exact moment |
|
247 | - // because the TXN is active in another request |
|
248 | - EE_Cron_Tasks::schedule_update_transaction_with_payment( |
|
249 | - time(), |
|
250 | - $transaction->ID(), |
|
251 | - $payment->ID() |
|
252 | - ); |
|
253 | - } else { |
|
254 | - // verify payment and that it has been saved |
|
255 | - if ($payment instanceof EE_Payment) { |
|
256 | - $do_action = $this->processRegistrationPayments($transaction, $payment, $do_action); |
|
257 | - $this->updateTransactionAndPayment($transaction, $payment, $update_txn, $IPN); |
|
258 | - } |
|
259 | - // granular hook for others to use. |
|
260 | - do_action($do_action, $transaction, $payment); |
|
261 | - do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action'); |
|
262 | - // global hook for others to use. |
|
263 | - do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment); |
|
264 | - } |
|
265 | - } |
|
202 | + /** |
|
203 | + * This should be called each time there may have been an update to a |
|
204 | + * payment on a transaction (ie, we asked for a payment to process a |
|
205 | + * payment for a transaction, or we told a payment method about an IPN, or |
|
206 | + * we told a payment method to |
|
207 | + * "finalize_payment_for" (a transaction), or we told a payment method to |
|
208 | + * process a refund. This should handle firing the correct hooks to |
|
209 | + * indicate |
|
210 | + * what exactly happened and updating the transaction appropriately). This |
|
211 | + * could be integrated directly into EE_Transaction upon save, but we want |
|
212 | + * this logic to be separate from 'normal' plain-jane saving and updating |
|
213 | + * of transactions and payments, and to be tied to payment processing. |
|
214 | + * Note: this method DOES NOT save the payment passed into it. It is the responsibility |
|
215 | + * of previous code to decide whether to save (because the payment passed into |
|
216 | + * this method might be a temporary, never-to-be-saved payment from an offline gateway, |
|
217 | + * in which case we only want that payment object for some temporary usage during this request, |
|
218 | + * but we don't want it to be saved). |
|
219 | + * |
|
220 | + * @param EE_Transaction $transaction |
|
221 | + * @param EE_Payment|null $payment |
|
222 | + * @param bool $update_txn whether to call EE_Transaction_Processor:: |
|
223 | + * update_transaction_and_registrations_after_checkout_or_payment() |
|
224 | + * (you can save 1 DB query if you know you're going |
|
225 | + * to save it later instead) |
|
226 | + * @param bool $IPN |
|
227 | + * if processing IPNs or other similar payment |
|
228 | + * related activities that occur in alternate |
|
229 | + * requests than the main one that is processing the |
|
230 | + * TXN, then set this to true to check whether the |
|
231 | + * TXN is locked before updating |
|
232 | + * @throws EE_Error |
|
233 | + * @throws ReflectionException |
|
234 | + */ |
|
235 | + public function updateTransactionBasedOnPayment( |
|
236 | + EE_Transaction $transaction, |
|
237 | + ?EE_Payment $payment, |
|
238 | + bool $update_txn = true, |
|
239 | + bool $IPN = false |
|
240 | + ) { |
|
241 | + $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful'; |
|
242 | + /** @type EE_Transaction $transaction */ |
|
243 | + $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction); |
|
244 | + // can we freely update the TXN at this moment? |
|
245 | + if ($IPN && $transaction->is_locked()) { |
|
246 | + // don't update the transaction at this exact moment |
|
247 | + // because the TXN is active in another request |
|
248 | + EE_Cron_Tasks::schedule_update_transaction_with_payment( |
|
249 | + time(), |
|
250 | + $transaction->ID(), |
|
251 | + $payment->ID() |
|
252 | + ); |
|
253 | + } else { |
|
254 | + // verify payment and that it has been saved |
|
255 | + if ($payment instanceof EE_Payment) { |
|
256 | + $do_action = $this->processRegistrationPayments($transaction, $payment, $do_action); |
|
257 | + $this->updateTransactionAndPayment($transaction, $payment, $update_txn, $IPN); |
|
258 | + } |
|
259 | + // granular hook for others to use. |
|
260 | + do_action($do_action, $transaction, $payment); |
|
261 | + do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action'); |
|
262 | + // global hook for others to use. |
|
263 | + do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment); |
|
264 | + } |
|
265 | + } |
|
266 | 266 | |
267 | 267 | |
268 | - /** |
|
269 | - * @param EE_Transaction $transaction |
|
270 | - * @param EE_Payment $payment |
|
271 | - * @param string $do_action |
|
272 | - * @return string |
|
273 | - * @throws EE_Error |
|
274 | - * @throws ReflectionException |
|
275 | - */ |
|
276 | - private function processRegistrationPayments( |
|
277 | - EE_Transaction $transaction, |
|
278 | - EE_Payment $payment, |
|
279 | - string $do_action |
|
280 | - ): string { |
|
281 | - if ($payment->ID()) { |
|
282 | - if ( |
|
283 | - $payment->payment_method() instanceof EE_Payment_Method |
|
284 | - && $payment->payment_method()->type_obj() instanceof EE_PMT_Base |
|
285 | - ) { |
|
286 | - $payment->payment_method()->type_obj()->update_txn_based_on_payment($payment); |
|
287 | - // update TXN registrations with payment info |
|
288 | - $this->registration_payments->processRegistrationPayments($transaction, $payment); |
|
289 | - } |
|
290 | - return $payment->just_approved() |
|
291 | - ? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful' |
|
292 | - : $do_action; |
|
293 | - } |
|
294 | - // send out notifications |
|
295 | - add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true'); |
|
296 | - return 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made'; |
|
297 | - } |
|
268 | + /** |
|
269 | + * @param EE_Transaction $transaction |
|
270 | + * @param EE_Payment $payment |
|
271 | + * @param string $do_action |
|
272 | + * @return string |
|
273 | + * @throws EE_Error |
|
274 | + * @throws ReflectionException |
|
275 | + */ |
|
276 | + private function processRegistrationPayments( |
|
277 | + EE_Transaction $transaction, |
|
278 | + EE_Payment $payment, |
|
279 | + string $do_action |
|
280 | + ): string { |
|
281 | + if ($payment->ID()) { |
|
282 | + if ( |
|
283 | + $payment->payment_method() instanceof EE_Payment_Method |
|
284 | + && $payment->payment_method()->type_obj() instanceof EE_PMT_Base |
|
285 | + ) { |
|
286 | + $payment->payment_method()->type_obj()->update_txn_based_on_payment($payment); |
|
287 | + // update TXN registrations with payment info |
|
288 | + $this->registration_payments->processRegistrationPayments($transaction, $payment); |
|
289 | + } |
|
290 | + return $payment->just_approved() |
|
291 | + ? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful' |
|
292 | + : $do_action; |
|
293 | + } |
|
294 | + // send out notifications |
|
295 | + add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true'); |
|
296 | + return 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made'; |
|
297 | + } |
|
298 | 298 | |
299 | 299 | |
300 | - /** |
|
301 | - * @param EE_Transaction $transaction |
|
302 | - * @param EE_Payment $payment |
|
303 | - * @param bool $update_txn |
|
304 | - * @param bool $IPN |
|
305 | - * @return void |
|
306 | - * @throws EE_Error |
|
307 | - * @throws ReflectionException |
|
308 | - */ |
|
309 | - private function updateTransactionAndPayment( |
|
310 | - EE_Transaction $transaction, |
|
311 | - EE_Payment $payment, |
|
312 | - bool $update_txn, |
|
313 | - bool $IPN |
|
314 | - ) { |
|
315 | - if ($payment->status() !== EEM_Payment::status_id_failed) { |
|
316 | - /** @type EE_Transaction_Payments $transaction_payments */ |
|
317 | - $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments'); |
|
318 | - // set new value for total paid |
|
319 | - $transaction_payments->calculate_total_payments_and_update_status($transaction); |
|
320 | - if ($update_txn) { |
|
321 | - $this->post_payment_processor->updateTransactionAndPayment($transaction, $payment, $IPN); |
|
322 | - } |
|
323 | - } |
|
324 | - } |
|
300 | + /** |
|
301 | + * @param EE_Transaction $transaction |
|
302 | + * @param EE_Payment $payment |
|
303 | + * @param bool $update_txn |
|
304 | + * @param bool $IPN |
|
305 | + * @return void |
|
306 | + * @throws EE_Error |
|
307 | + * @throws ReflectionException |
|
308 | + */ |
|
309 | + private function updateTransactionAndPayment( |
|
310 | + EE_Transaction $transaction, |
|
311 | + EE_Payment $payment, |
|
312 | + bool $update_txn, |
|
313 | + bool $IPN |
|
314 | + ) { |
|
315 | + if ($payment->status() !== EEM_Payment::status_id_failed) { |
|
316 | + /** @type EE_Transaction_Payments $transaction_payments */ |
|
317 | + $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments'); |
|
318 | + // set new value for total paid |
|
319 | + $transaction_payments->calculate_total_payments_and_update_status($transaction); |
|
320 | + if ($update_txn) { |
|
321 | + $this->post_payment_processor->updateTransactionAndPayment($transaction, $payment, $IPN); |
|
322 | + } |
|
323 | + } |
|
324 | + } |
|
325 | 325 | |
326 | 326 | |
327 | - /** |
|
328 | - * @throws EE_Error |
|
329 | - * @throws ReflectionException |
|
330 | - */ |
|
331 | - private function validatePaymentAmount(EE_Transaction $transaction, float $amount) |
|
332 | - { |
|
333 | - if ($amount < 0) { |
|
334 | - throw new EE_Error( |
|
335 | - sprintf( |
|
336 | - esc_html__( |
|
337 | - 'Attempting to make a payment for a negative amount of %1$d for transaction %2$d. That should be a refund', |
|
338 | - 'event_espresso' |
|
339 | - ), |
|
340 | - $amount, |
|
341 | - $transaction->ID() |
|
342 | - ) |
|
343 | - ); |
|
344 | - } |
|
345 | - } |
|
327 | + /** |
|
328 | + * @throws EE_Error |
|
329 | + * @throws ReflectionException |
|
330 | + */ |
|
331 | + private function validatePaymentAmount(EE_Transaction $transaction, float $amount) |
|
332 | + { |
|
333 | + if ($amount < 0) { |
|
334 | + throw new EE_Error( |
|
335 | + sprintf( |
|
336 | + esc_html__( |
|
337 | + 'Attempting to make a payment for a negative amount of %1$d for transaction %2$d. That should be a refund', |
|
338 | + 'event_espresso' |
|
339 | + ), |
|
340 | + $amount, |
|
341 | + $transaction->ID() |
|
342 | + ) |
|
343 | + ); |
|
344 | + } |
|
345 | + } |
|
346 | 346 | } |
@@ -21,107 +21,107 @@ |
||
21 | 21 | */ |
22 | 22 | class PostPaymentProcessor |
23 | 23 | { |
24 | - private EE_Transaction_Processor $transaction_processor; |
|
24 | + private EE_Transaction_Processor $transaction_processor; |
|
25 | 25 | |
26 | 26 | |
27 | - /** |
|
28 | - * @param EE_Transaction_Processor $transaction_processor |
|
29 | - */ |
|
30 | - public function __construct(EE_Transaction_Processor $transaction_processor) |
|
31 | - { |
|
32 | - $this->transaction_processor = $transaction_processor; |
|
33 | - } |
|
27 | + /** |
|
28 | + * @param EE_Transaction_Processor $transaction_processor |
|
29 | + */ |
|
30 | + public function __construct(EE_Transaction_Processor $transaction_processor) |
|
31 | + { |
|
32 | + $this->transaction_processor = $transaction_processor; |
|
33 | + } |
|
34 | 34 | |
35 | 35 | |
36 | - /** |
|
37 | - * Process payments and transaction after payment process completed. |
|
38 | - * ultimately this will send the TXN and payment details off so that notifications can be sent out. |
|
39 | - * if this request happens to be processing an IPN, |
|
40 | - * then we will also set the Payment Options Reg Step to completed, |
|
41 | - * and attempt to completely finalize the TXN if all the other Reg Steps are completed as well. |
|
42 | - * |
|
43 | - * @param EE_Transaction $transaction |
|
44 | - * @param EE_Payment $payment |
|
45 | - * @param bool $IPN |
|
46 | - * @throws EE_Error |
|
47 | - * @throws ReflectionException |
|
48 | - */ |
|
49 | - public function updateTransactionAndPayment(EE_Transaction $transaction, EE_Payment $payment, bool $IPN = false) |
|
50 | - { |
|
51 | - // is the Payment Options Reg Step completed ? |
|
52 | - $payment_options_step_completed = $transaction->reg_step_completed('payment_options'); |
|
53 | - // if the Payment Options Reg Step is completed... |
|
54 | - // then this is kinda sorta a revisit with regard to payments at least |
|
55 | - $this->transaction_processor->set_revisit($payment_options_step_completed === true); |
|
56 | - // if this is an IPN, let's consider the Payment Options Reg Step completed if not already |
|
57 | - if ( |
|
58 | - $IPN |
|
59 | - && $payment_options_step_completed !== true |
|
60 | - && ($payment->is_approved() || $payment->is_pending()) |
|
61 | - ) { |
|
62 | - $payment_options_step_completed = $transaction->set_reg_step_completed( |
|
63 | - 'payment_options' |
|
64 | - ); |
|
65 | - } |
|
66 | - // maybe update status, but don't save transaction just yet |
|
67 | - $transaction->update_status_based_on_total_paid(false); |
|
68 | - // check if 'finalize_registration' step has been completed... |
|
69 | - $finalized = $transaction->reg_step_completed('finalize_registration'); |
|
70 | - // if this is an IPN and the final step has not been initiated |
|
71 | - if ($IPN && $payment_options_step_completed && $finalized === false) { |
|
72 | - // and if it hasn't already been set as being started... |
|
73 | - $finalized = $transaction->set_reg_step_initiated('finalize_registration'); |
|
74 | - } |
|
75 | - $transaction->save(); |
|
76 | - // because the above will return false if the final step was not fully completed, we need to check again... |
|
77 | - if ($IPN && $finalized !== false) { |
|
78 | - // and if we are all good to go, then send out notifications |
|
79 | - add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true'); |
|
80 | - // ok, now process the transaction according to the payment |
|
81 | - $this->transaction_processor->update_transaction_and_registrations_after_checkout_or_payment( |
|
82 | - $transaction, |
|
83 | - $payment |
|
84 | - ); |
|
85 | - } |
|
86 | - // DEBUG LOG |
|
87 | - $this->debugLog($transaction, $payment, $IPN, $finalized); |
|
88 | - } |
|
36 | + /** |
|
37 | + * Process payments and transaction after payment process completed. |
|
38 | + * ultimately this will send the TXN and payment details off so that notifications can be sent out. |
|
39 | + * if this request happens to be processing an IPN, |
|
40 | + * then we will also set the Payment Options Reg Step to completed, |
|
41 | + * and attempt to completely finalize the TXN if all the other Reg Steps are completed as well. |
|
42 | + * |
|
43 | + * @param EE_Transaction $transaction |
|
44 | + * @param EE_Payment $payment |
|
45 | + * @param bool $IPN |
|
46 | + * @throws EE_Error |
|
47 | + * @throws ReflectionException |
|
48 | + */ |
|
49 | + public function updateTransactionAndPayment(EE_Transaction $transaction, EE_Payment $payment, bool $IPN = false) |
|
50 | + { |
|
51 | + // is the Payment Options Reg Step completed ? |
|
52 | + $payment_options_step_completed = $transaction->reg_step_completed('payment_options'); |
|
53 | + // if the Payment Options Reg Step is completed... |
|
54 | + // then this is kinda sorta a revisit with regard to payments at least |
|
55 | + $this->transaction_processor->set_revisit($payment_options_step_completed === true); |
|
56 | + // if this is an IPN, let's consider the Payment Options Reg Step completed if not already |
|
57 | + if ( |
|
58 | + $IPN |
|
59 | + && $payment_options_step_completed !== true |
|
60 | + && ($payment->is_approved() || $payment->is_pending()) |
|
61 | + ) { |
|
62 | + $payment_options_step_completed = $transaction->set_reg_step_completed( |
|
63 | + 'payment_options' |
|
64 | + ); |
|
65 | + } |
|
66 | + // maybe update status, but don't save transaction just yet |
|
67 | + $transaction->update_status_based_on_total_paid(false); |
|
68 | + // check if 'finalize_registration' step has been completed... |
|
69 | + $finalized = $transaction->reg_step_completed('finalize_registration'); |
|
70 | + // if this is an IPN and the final step has not been initiated |
|
71 | + if ($IPN && $payment_options_step_completed && $finalized === false) { |
|
72 | + // and if it hasn't already been set as being started... |
|
73 | + $finalized = $transaction->set_reg_step_initiated('finalize_registration'); |
|
74 | + } |
|
75 | + $transaction->save(); |
|
76 | + // because the above will return false if the final step was not fully completed, we need to check again... |
|
77 | + if ($IPN && $finalized !== false) { |
|
78 | + // and if we are all good to go, then send out notifications |
|
79 | + add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true'); |
|
80 | + // ok, now process the transaction according to the payment |
|
81 | + $this->transaction_processor->update_transaction_and_registrations_after_checkout_or_payment( |
|
82 | + $transaction, |
|
83 | + $payment |
|
84 | + ); |
|
85 | + } |
|
86 | + // DEBUG LOG |
|
87 | + $this->debugLog($transaction, $payment, $IPN, $finalized); |
|
88 | + } |
|
89 | 89 | |
90 | 90 | |
91 | - /** |
|
92 | - * @param EE_Transaction $transaction |
|
93 | - * @param EE_Payment $payment |
|
94 | - * @param bool $IPN |
|
95 | - * @param bool $finalized |
|
96 | - * @return void |
|
97 | - * @throws EE_Error |
|
98 | - * @throws ReflectionException |
|
99 | - */ |
|
100 | - private function debugLog(EE_Transaction $transaction, EE_Payment $payment, bool $IPN, bool $finalized) |
|
101 | - { |
|
102 | - $payment_method = $payment->payment_method(); |
|
103 | - if ($payment_method instanceof EE_Payment_Method) { |
|
104 | - $payment_method_type_obj = $payment_method->type_obj(); |
|
105 | - if ($payment_method_type_obj instanceof EE_PMT_Base) { |
|
106 | - $gateway = $payment_method_type_obj->get_gateway(); |
|
107 | - if ($gateway instanceof EE_Gateway) { |
|
108 | - $gateway->log( |
|
109 | - [ |
|
110 | - 'message' => esc_html__( |
|
111 | - 'Post Payment Transaction Details', |
|
112 | - 'event_espresso' |
|
113 | - ), |
|
114 | - 'transaction' => $transaction->model_field_array(), |
|
115 | - 'finalized' => $finalized, |
|
116 | - 'IPN' => $IPN, |
|
117 | - 'deliver_notifications' => has_filter( |
|
118 | - 'FHEE__EED_Messages___maybe_registration__deliver_notifications' |
|
119 | - ), |
|
120 | - ], |
|
121 | - $payment |
|
122 | - ); |
|
123 | - } |
|
124 | - } |
|
125 | - } |
|
126 | - } |
|
91 | + /** |
|
92 | + * @param EE_Transaction $transaction |
|
93 | + * @param EE_Payment $payment |
|
94 | + * @param bool $IPN |
|
95 | + * @param bool $finalized |
|
96 | + * @return void |
|
97 | + * @throws EE_Error |
|
98 | + * @throws ReflectionException |
|
99 | + */ |
|
100 | + private function debugLog(EE_Transaction $transaction, EE_Payment $payment, bool $IPN, bool $finalized) |
|
101 | + { |
|
102 | + $payment_method = $payment->payment_method(); |
|
103 | + if ($payment_method instanceof EE_Payment_Method) { |
|
104 | + $payment_method_type_obj = $payment_method->type_obj(); |
|
105 | + if ($payment_method_type_obj instanceof EE_PMT_Base) { |
|
106 | + $gateway = $payment_method_type_obj->get_gateway(); |
|
107 | + if ($gateway instanceof EE_Gateway) { |
|
108 | + $gateway->log( |
|
109 | + [ |
|
110 | + 'message' => esc_html__( |
|
111 | + 'Post Payment Transaction Details', |
|
112 | + 'event_espresso' |
|
113 | + ), |
|
114 | + 'transaction' => $transaction->model_field_array(), |
|
115 | + 'finalized' => $finalized, |
|
116 | + 'IPN' => $IPN, |
|
117 | + 'deliver_notifications' => has_filter( |
|
118 | + 'FHEE__EED_Messages___maybe_registration__deliver_notifications' |
|
119 | + ), |
|
120 | + ], |
|
121 | + $payment |
|
122 | + ); |
|
123 | + } |
|
124 | + } |
|
125 | + } |
|
126 | + } |
|
127 | 127 | } |
@@ -28,162 +28,162 @@ |
||
28 | 28 | */ |
29 | 29 | class PaymentProcessorFees |
30 | 30 | { |
31 | - public const GATEWAY_PAYPAL = 'PayPal Commerce'; |
|
32 | - |
|
33 | - public const GATEWAY_SQUARE = 'Square'; |
|
34 | - |
|
35 | - public const GATEWAY_STRIPE = 'Stripe'; |
|
36 | - |
|
37 | - private GracePeriod $grace_period; |
|
38 | - |
|
39 | - private LicenseData $license_data; |
|
40 | - |
|
41 | - /** |
|
42 | - * @var float[][] $gateway_fees |
|
43 | - */ |
|
44 | - private array $gateway_fees = [ |
|
45 | - LicenseData::LICENSE_ACTIVE => [ |
|
46 | - PaymentProcessorFees::GATEWAY_PAYPAL => 0.00, |
|
47 | - PaymentProcessorFees::GATEWAY_SQUARE => 0.00, |
|
48 | - PaymentProcessorFees::GATEWAY_STRIPE => 0.00, |
|
49 | - ], |
|
50 | - LicenseData::LICENSE_EXPIRED => [ |
|
51 | - PaymentProcessorFees::GATEWAY_PAYPAL => 3.00, |
|
52 | - PaymentProcessorFees::GATEWAY_SQUARE => 2.50, |
|
53 | - PaymentProcessorFees::GATEWAY_STRIPE => 2.50, |
|
54 | - ], |
|
55 | - LicenseData::LICENSE_DECAF => [ |
|
56 | - PaymentProcessorFees::GATEWAY_PAYPAL => 3.00, |
|
57 | - PaymentProcessorFees::GATEWAY_SQUARE => 2.50, |
|
58 | - PaymentProcessorFees::GATEWAY_STRIPE => 2.50, |
|
59 | - ], |
|
60 | - ]; |
|
61 | - |
|
62 | - private array $partner_gateways = [ |
|
63 | - PaymentProcessorFees::GATEWAY_PAYPAL, |
|
64 | - PaymentProcessorFees::GATEWAY_SQUARE, |
|
65 | - PaymentProcessorFees::GATEWAY_STRIPE, |
|
66 | - ]; |
|
67 | - |
|
68 | - |
|
69 | - /** |
|
70 | - * @param GracePeriod $grace_period |
|
71 | - * @param LicenseData $license_data |
|
72 | - */ |
|
73 | - public function __construct(GracePeriod $grace_period, LicenseData $license_data) |
|
74 | - { |
|
75 | - $this->grace_period = $grace_period; |
|
76 | - $this->license_data = $license_data; |
|
77 | - } |
|
78 | - |
|
79 | - |
|
80 | - /** |
|
81 | - * @param EE_Transaction $transaction |
|
82 | - * @param string $payment_method_name |
|
83 | - * @param float $amount |
|
84 | - * @return float |
|
85 | - * @throws EE_Error|ReflectionException|RuntimeException |
|
86 | - * @throws Exception |
|
87 | - */ |
|
88 | - public function applyGatewayPartnerFees( |
|
89 | - EE_Transaction $transaction, |
|
90 | - string $payment_method_name, |
|
91 | - float $amount = 0.00 |
|
92 | - ): float { |
|
93 | - $processing_fee = $this->forPaymentMethod($payment_method_name); |
|
94 | - if ($processing_fee <= 0.00) { |
|
95 | - return $amount; |
|
96 | - } |
|
97 | - $grand_total = $transaction->total_line_item(false); |
|
98 | - if ( |
|
99 | - ! $grand_total instanceof EE_Line_Item |
|
100 | - || ! $grand_total->is_total() |
|
101 | - || $grand_total->TXN_ID() !== $transaction->ID() |
|
102 | - ) { |
|
103 | - // throw RuntimeException if total_line_item is not a total line item |
|
104 | - throw new RuntimeException( |
|
105 | - sprintf( |
|
106 | - esc_html__( |
|
107 | - 'Invalid or missing grand total line item for transaction %1$d.', |
|
108 | - 'event_espresso' |
|
109 | - ), |
|
110 | - $transaction->ID() |
|
111 | - ) |
|
112 | - ); |
|
113 | - } |
|
114 | - $line_item = EEH_Line_Item::add_percentage_based_item( |
|
115 | - EEH_Line_Item::get_pre_tax_subtotal($grand_total), |
|
116 | - esc_html__('Payment Processing Fee', 'event_espresso'), |
|
117 | - $processing_fee, |
|
118 | - '', |
|
119 | - false, |
|
120 | - sanitize_key( |
|
121 | - sprintf( |
|
122 | - '%1$s-fee-%2$d', |
|
123 | - $payment_method_name, |
|
124 | - $transaction->ID() |
|
125 | - ) |
|
126 | - ), |
|
127 | - true |
|
128 | - ); |
|
129 | - $line_item->save(); |
|
130 | - return $grand_total->recalculate_total_including_taxes(); |
|
131 | - } |
|
132 | - |
|
133 | - |
|
134 | - /** |
|
135 | - * Returns the fee for a specific payment method based on the license status. |
|
136 | - * |
|
137 | - * @param string $payment_method_name |
|
138 | - * @return float |
|
139 | - * @throws Exception|OutOfBoundsException|RuntimeException |
|
140 | - */ |
|
141 | - public function forPaymentMethod(string $payment_method_name): float |
|
142 | - { |
|
143 | - if (! $this->isPartnerGateway($payment_method_name)) { |
|
144 | - return 0.0; |
|
145 | - } |
|
146 | - $license_status = $this->license_data->licenseStatus(); |
|
147 | - $license_expires = $this->license_data->licenseExpiry(); |
|
148 | - // decaf, new activations, or expired licenses are allowed a grace period |
|
149 | - if ($this->grace_period->withinGracePeriod($license_status, $license_expires)) { |
|
150 | - return 0.0; |
|
151 | - } |
|
152 | - return $this->getGatewayFee($payment_method_name, $license_status); |
|
153 | - } |
|
154 | - |
|
155 | - |
|
156 | - /** |
|
157 | - * Checks if a gateway is a partner gateway. |
|
158 | - * |
|
159 | - * @param string $payment_method_name |
|
160 | - * @return bool |
|
161 | - */ |
|
162 | - private function isPartnerGateway(string $payment_method_name): bool |
|
163 | - { |
|
164 | - return in_array($payment_method_name, $this->partner_gateways, true); |
|
165 | - } |
|
166 | - |
|
167 | - |
|
168 | - /** |
|
169 | - * Returns the fee for a specific payment method based on the license status. |
|
170 | - * |
|
171 | - * @param string $payment_method_name |
|
172 | - * @param string $license_status |
|
173 | - * @return float |
|
174 | - * @throws OutOfBoundsException |
|
175 | - */ |
|
176 | - private function getGatewayFee(string $payment_method_name, string $license_status): float |
|
177 | - { |
|
178 | - if (isset($this->gateway_fees[ $license_status ][ $payment_method_name ] )) { |
|
179 | - return $this->gateway_fees[ $license_status ][ $payment_method_name ]; |
|
180 | - } |
|
181 | - throw new OutOfBoundsException( |
|
182 | - sprintf( |
|
183 | - esc_html__('A partner fee for %1$s with %2$s license is not defined.', 'event_espresso'), |
|
184 | - $payment_method_name, |
|
185 | - $license_status |
|
186 | - ) |
|
187 | - ); |
|
188 | - } |
|
31 | + public const GATEWAY_PAYPAL = 'PayPal Commerce'; |
|
32 | + |
|
33 | + public const GATEWAY_SQUARE = 'Square'; |
|
34 | + |
|
35 | + public const GATEWAY_STRIPE = 'Stripe'; |
|
36 | + |
|
37 | + private GracePeriod $grace_period; |
|
38 | + |
|
39 | + private LicenseData $license_data; |
|
40 | + |
|
41 | + /** |
|
42 | + * @var float[][] $gateway_fees |
|
43 | + */ |
|
44 | + private array $gateway_fees = [ |
|
45 | + LicenseData::LICENSE_ACTIVE => [ |
|
46 | + PaymentProcessorFees::GATEWAY_PAYPAL => 0.00, |
|
47 | + PaymentProcessorFees::GATEWAY_SQUARE => 0.00, |
|
48 | + PaymentProcessorFees::GATEWAY_STRIPE => 0.00, |
|
49 | + ], |
|
50 | + LicenseData::LICENSE_EXPIRED => [ |
|
51 | + PaymentProcessorFees::GATEWAY_PAYPAL => 3.00, |
|
52 | + PaymentProcessorFees::GATEWAY_SQUARE => 2.50, |
|
53 | + PaymentProcessorFees::GATEWAY_STRIPE => 2.50, |
|
54 | + ], |
|
55 | + LicenseData::LICENSE_DECAF => [ |
|
56 | + PaymentProcessorFees::GATEWAY_PAYPAL => 3.00, |
|
57 | + PaymentProcessorFees::GATEWAY_SQUARE => 2.50, |
|
58 | + PaymentProcessorFees::GATEWAY_STRIPE => 2.50, |
|
59 | + ], |
|
60 | + ]; |
|
61 | + |
|
62 | + private array $partner_gateways = [ |
|
63 | + PaymentProcessorFees::GATEWAY_PAYPAL, |
|
64 | + PaymentProcessorFees::GATEWAY_SQUARE, |
|
65 | + PaymentProcessorFees::GATEWAY_STRIPE, |
|
66 | + ]; |
|
67 | + |
|
68 | + |
|
69 | + /** |
|
70 | + * @param GracePeriod $grace_period |
|
71 | + * @param LicenseData $license_data |
|
72 | + */ |
|
73 | + public function __construct(GracePeriod $grace_period, LicenseData $license_data) |
|
74 | + { |
|
75 | + $this->grace_period = $grace_period; |
|
76 | + $this->license_data = $license_data; |
|
77 | + } |
|
78 | + |
|
79 | + |
|
80 | + /** |
|
81 | + * @param EE_Transaction $transaction |
|
82 | + * @param string $payment_method_name |
|
83 | + * @param float $amount |
|
84 | + * @return float |
|
85 | + * @throws EE_Error|ReflectionException|RuntimeException |
|
86 | + * @throws Exception |
|
87 | + */ |
|
88 | + public function applyGatewayPartnerFees( |
|
89 | + EE_Transaction $transaction, |
|
90 | + string $payment_method_name, |
|
91 | + float $amount = 0.00 |
|
92 | + ): float { |
|
93 | + $processing_fee = $this->forPaymentMethod($payment_method_name); |
|
94 | + if ($processing_fee <= 0.00) { |
|
95 | + return $amount; |
|
96 | + } |
|
97 | + $grand_total = $transaction->total_line_item(false); |
|
98 | + if ( |
|
99 | + ! $grand_total instanceof EE_Line_Item |
|
100 | + || ! $grand_total->is_total() |
|
101 | + || $grand_total->TXN_ID() !== $transaction->ID() |
|
102 | + ) { |
|
103 | + // throw RuntimeException if total_line_item is not a total line item |
|
104 | + throw new RuntimeException( |
|
105 | + sprintf( |
|
106 | + esc_html__( |
|
107 | + 'Invalid or missing grand total line item for transaction %1$d.', |
|
108 | + 'event_espresso' |
|
109 | + ), |
|
110 | + $transaction->ID() |
|
111 | + ) |
|
112 | + ); |
|
113 | + } |
|
114 | + $line_item = EEH_Line_Item::add_percentage_based_item( |
|
115 | + EEH_Line_Item::get_pre_tax_subtotal($grand_total), |
|
116 | + esc_html__('Payment Processing Fee', 'event_espresso'), |
|
117 | + $processing_fee, |
|
118 | + '', |
|
119 | + false, |
|
120 | + sanitize_key( |
|
121 | + sprintf( |
|
122 | + '%1$s-fee-%2$d', |
|
123 | + $payment_method_name, |
|
124 | + $transaction->ID() |
|
125 | + ) |
|
126 | + ), |
|
127 | + true |
|
128 | + ); |
|
129 | + $line_item->save(); |
|
130 | + return $grand_total->recalculate_total_including_taxes(); |
|
131 | + } |
|
132 | + |
|
133 | + |
|
134 | + /** |
|
135 | + * Returns the fee for a specific payment method based on the license status. |
|
136 | + * |
|
137 | + * @param string $payment_method_name |
|
138 | + * @return float |
|
139 | + * @throws Exception|OutOfBoundsException|RuntimeException |
|
140 | + */ |
|
141 | + public function forPaymentMethod(string $payment_method_name): float |
|
142 | + { |
|
143 | + if (! $this->isPartnerGateway($payment_method_name)) { |
|
144 | + return 0.0; |
|
145 | + } |
|
146 | + $license_status = $this->license_data->licenseStatus(); |
|
147 | + $license_expires = $this->license_data->licenseExpiry(); |
|
148 | + // decaf, new activations, or expired licenses are allowed a grace period |
|
149 | + if ($this->grace_period->withinGracePeriod($license_status, $license_expires)) { |
|
150 | + return 0.0; |
|
151 | + } |
|
152 | + return $this->getGatewayFee($payment_method_name, $license_status); |
|
153 | + } |
|
154 | + |
|
155 | + |
|
156 | + /** |
|
157 | + * Checks if a gateway is a partner gateway. |
|
158 | + * |
|
159 | + * @param string $payment_method_name |
|
160 | + * @return bool |
|
161 | + */ |
|
162 | + private function isPartnerGateway(string $payment_method_name): bool |
|
163 | + { |
|
164 | + return in_array($payment_method_name, $this->partner_gateways, true); |
|
165 | + } |
|
166 | + |
|
167 | + |
|
168 | + /** |
|
169 | + * Returns the fee for a specific payment method based on the license status. |
|
170 | + * |
|
171 | + * @param string $payment_method_name |
|
172 | + * @param string $license_status |
|
173 | + * @return float |
|
174 | + * @throws OutOfBoundsException |
|
175 | + */ |
|
176 | + private function getGatewayFee(string $payment_method_name, string $license_status): float |
|
177 | + { |
|
178 | + if (isset($this->gateway_fees[ $license_status ][ $payment_method_name ] )) { |
|
179 | + return $this->gateway_fees[ $license_status ][ $payment_method_name ]; |
|
180 | + } |
|
181 | + throw new OutOfBoundsException( |
|
182 | + sprintf( |
|
183 | + esc_html__('A partner fee for %1$s with %2$s license is not defined.', 'event_espresso'), |
|
184 | + $payment_method_name, |
|
185 | + $license_status |
|
186 | + ) |
|
187 | + ); |
|
188 | + } |
|
189 | 189 | } |
@@ -140,7 +140,7 @@ discard block |
||
140 | 140 | */ |
141 | 141 | public function forPaymentMethod(string $payment_method_name): float |
142 | 142 | { |
143 | - if (! $this->isPartnerGateway($payment_method_name)) { |
|
143 | + if ( ! $this->isPartnerGateway($payment_method_name)) { |
|
144 | 144 | return 0.0; |
145 | 145 | } |
146 | 146 | $license_status = $this->license_data->licenseStatus(); |
@@ -175,8 +175,8 @@ discard block |
||
175 | 175 | */ |
176 | 176 | private function getGatewayFee(string $payment_method_name, string $license_status): float |
177 | 177 | { |
178 | - if (isset($this->gateway_fees[ $license_status ][ $payment_method_name ] )) { |
|
179 | - return $this->gateway_fees[ $license_status ][ $payment_method_name ]; |
|
178 | + if (isset($this->gateway_fees[$license_status][$payment_method_name])) { |
|
179 | + return $this->gateway_fees[$license_status][$payment_method_name]; |
|
180 | 180 | } |
181 | 181 | throw new OutOfBoundsException( |
182 | 182 | sprintf( |
@@ -16,74 +16,74 @@ |
||
16 | 16 | */ |
17 | 17 | class LicenseKeyData extends WordPressOption |
18 | 18 | { |
19 | - /** |
|
20 | - * The name of the WordPress option where license data is stored. |
|
21 | - */ |
|
22 | - const OPTION_NAME = 'event-espresso-license-keys'; |
|
19 | + /** |
|
20 | + * The name of the WordPress option where license data is stored. |
|
21 | + */ |
|
22 | + const OPTION_NAME = 'event-espresso-license-keys'; |
|
23 | 23 | |
24 | 24 | |
25 | - public function __construct() |
|
26 | - { |
|
27 | - parent::__construct(LicenseKeyData::OPTION_NAME, [], true); |
|
28 | - } |
|
25 | + public function __construct() |
|
26 | + { |
|
27 | + parent::__construct(LicenseKeyData::OPTION_NAME, [], true); |
|
28 | + } |
|
29 | 29 | |
30 | 30 | |
31 | - /** |
|
32 | - * Retrieves the license data for a specific plugin. |
|
33 | - * |
|
34 | - * @param string $plugin The plugin identifier. |
|
35 | - * @return stdClass An object containing the license data for the specified plugin. |
|
36 | - * Returns a default structure if no data exists. |
|
37 | - */ |
|
38 | - public function getLicenseDataForPlugin(string $plugin): stdCLass |
|
39 | - { |
|
40 | - $licenses = $this->loadOption(); |
|
41 | - $license_data = $licenses[ $plugin ] ?? ['license' => 'none', 'success' => false, 'error' => true]; |
|
42 | - return (object) $license_data; |
|
43 | - } |
|
31 | + /** |
|
32 | + * Retrieves the license data for a specific plugin. |
|
33 | + * |
|
34 | + * @param string $plugin The plugin identifier. |
|
35 | + * @return stdClass An object containing the license data for the specified plugin. |
|
36 | + * Returns a default structure if no data exists. |
|
37 | + */ |
|
38 | + public function getLicenseDataForPlugin(string $plugin): stdCLass |
|
39 | + { |
|
40 | + $licenses = $this->loadOption(); |
|
41 | + $license_data = $licenses[ $plugin ] ?? ['license' => 'none', 'success' => false, 'error' => true]; |
|
42 | + return (object) $license_data; |
|
43 | + } |
|
44 | 44 | |
45 | 45 | |
46 | - /** |
|
47 | - * Retrieves all stored license data for all plugins. |
|
48 | - * |
|
49 | - * @return array An associative array of all license data, keyed by plugin identifier. |
|
50 | - */ |
|
51 | - public function getAllLicenses(): array |
|
52 | - { |
|
53 | - return $this->loadOption(); |
|
54 | - } |
|
46 | + /** |
|
47 | + * Retrieves all stored license data for all plugins. |
|
48 | + * |
|
49 | + * @return array An associative array of all license data, keyed by plugin identifier. |
|
50 | + */ |
|
51 | + public function getAllLicenses(): array |
|
52 | + { |
|
53 | + return $this->loadOption(); |
|
54 | + } |
|
55 | 55 | |
56 | 56 | |
57 | - /** |
|
58 | - * Updates or adds the license data for a specific plugin. |
|
59 | - * |
|
60 | - * @param stdClass $license_data The new license data to store. |
|
61 | - * @param string $plugin The plugin identifier. |
|
62 | - * @param bool $force_update Whether to force the update operation. |
|
63 | - * @return int The result of the update operation. |
|
64 | - */ |
|
65 | - public function updateLicenseDataForPlugin(stdCLass $license_data, string $plugin, bool $force_update = false): int |
|
66 | - { |
|
67 | - $licenses = $this->loadOption(); |
|
68 | - // convert objects to array and merge new data with old |
|
69 | - $licenses[ $plugin ] = (array) $license_data; |
|
70 | - // then sort by key and convert back to stdCLass |
|
71 | - ksort($licenses[ $plugin ]); |
|
72 | - $licenses[ $plugin ] = (object) $licenses[ $plugin ]; |
|
73 | - return $this->updateOption($licenses, $force_update); |
|
74 | - } |
|
57 | + /** |
|
58 | + * Updates or adds the license data for a specific plugin. |
|
59 | + * |
|
60 | + * @param stdClass $license_data The new license data to store. |
|
61 | + * @param string $plugin The plugin identifier. |
|
62 | + * @param bool $force_update Whether to force the update operation. |
|
63 | + * @return int The result of the update operation. |
|
64 | + */ |
|
65 | + public function updateLicenseDataForPlugin(stdCLass $license_data, string $plugin, bool $force_update = false): int |
|
66 | + { |
|
67 | + $licenses = $this->loadOption(); |
|
68 | + // convert objects to array and merge new data with old |
|
69 | + $licenses[ $plugin ] = (array) $license_data; |
|
70 | + // then sort by key and convert back to stdCLass |
|
71 | + ksort($licenses[ $plugin ]); |
|
72 | + $licenses[ $plugin ] = (object) $licenses[ $plugin ]; |
|
73 | + return $this->updateOption($licenses, $force_update); |
|
74 | + } |
|
75 | 75 | |
76 | 76 | |
77 | - /** |
|
78 | - * Removes the license data for a specific plugin. |
|
79 | - * |
|
80 | - * @param string $plugin The plugin identifier. |
|
81 | - * @return int The result of the remove operation. |
|
82 | - */ |
|
83 | - public function removeLicenseDataForPlugin(string $plugin): int |
|
84 | - { |
|
85 | - $licenses = $this->loadOption(); |
|
86 | - unset($licenses[ $plugin ]); |
|
87 | - return $this->updateOption($licenses); |
|
88 | - } |
|
77 | + /** |
|
78 | + * Removes the license data for a specific plugin. |
|
79 | + * |
|
80 | + * @param string $plugin The plugin identifier. |
|
81 | + * @return int The result of the remove operation. |
|
82 | + */ |
|
83 | + public function removeLicenseDataForPlugin(string $plugin): int |
|
84 | + { |
|
85 | + $licenses = $this->loadOption(); |
|
86 | + unset($licenses[ $plugin ]); |
|
87 | + return $this->updateOption($licenses); |
|
88 | + } |
|
89 | 89 | } |
@@ -38,7 +38,7 @@ discard block |
||
38 | 38 | public function getLicenseDataForPlugin(string $plugin): stdCLass |
39 | 39 | { |
40 | 40 | $licenses = $this->loadOption(); |
41 | - $license_data = $licenses[ $plugin ] ?? ['license' => 'none', 'success' => false, 'error' => true]; |
|
41 | + $license_data = $licenses[$plugin] ?? ['license' => 'none', 'success' => false, 'error' => true]; |
|
42 | 42 | return (object) $license_data; |
43 | 43 | } |
44 | 44 | |
@@ -66,10 +66,10 @@ discard block |
||
66 | 66 | { |
67 | 67 | $licenses = $this->loadOption(); |
68 | 68 | // convert objects to array and merge new data with old |
69 | - $licenses[ $plugin ] = (array) $license_data; |
|
69 | + $licenses[$plugin] = (array) $license_data; |
|
70 | 70 | // then sort by key and convert back to stdCLass |
71 | - ksort($licenses[ $plugin ]); |
|
72 | - $licenses[ $plugin ] = (object) $licenses[ $plugin ]; |
|
71 | + ksort($licenses[$plugin]); |
|
72 | + $licenses[$plugin] = (object) $licenses[$plugin]; |
|
73 | 73 | return $this->updateOption($licenses, $force_update); |
74 | 74 | } |
75 | 75 | |
@@ -83,7 +83,7 @@ discard block |
||
83 | 83 | public function removeLicenseDataForPlugin(string $plugin): int |
84 | 84 | { |
85 | 85 | $licenses = $this->loadOption(); |
86 | - unset($licenses[ $plugin ]); |
|
86 | + unset($licenses[$plugin]); |
|
87 | 87 | return $this->updateOption($licenses); |
88 | 88 | } |
89 | 89 | } |
@@ -14,246 +14,246 @@ |
||
14 | 14 | */ |
15 | 15 | class PluginLicense |
16 | 16 | { |
17 | - private ?LicenseKeyData $license_key_data = null; |
|
18 | - |
|
19 | - private ?PluginUpdater $updater = null; |
|
20 | - |
|
21 | - private string $license_key = ''; |
|
22 | - |
|
23 | - private string $mainfile; |
|
24 | - |
|
25 | - private string $min_core_version; |
|
26 | - |
|
27 | - private string $plugin_id; |
|
28 | - |
|
29 | - private string $plugin_name; |
|
30 | - |
|
31 | - private string $plugin_slug; |
|
32 | - |
|
33 | - private string $version; |
|
34 | - |
|
35 | - private bool $beta; |
|
36 | - |
|
37 | - private bool $wp_override; |
|
38 | - |
|
39 | - private static array $slug_prefixes = ['ee-', 'eea-', 'eep-', 'espresso-', 'ee_', 'eea_', 'eep_', 'espresso_']; |
|
40 | - |
|
41 | - |
|
42 | - public function __construct( |
|
43 | - string $mainfile, |
|
44 | - string $plugin_id, |
|
45 | - string $plugin_name, |
|
46 | - string $plugin_slug, |
|
47 | - string $version, |
|
48 | - bool $beta = false, |
|
49 | - bool $wp_override = false, |
|
50 | - string $min_core_version = '' |
|
51 | - ) { |
|
52 | - $this->mainfile = $mainfile; |
|
53 | - $this->plugin_id = $plugin_id; |
|
54 | - $this->plugin_name = $plugin_name; |
|
55 | - $this->version = $version; |
|
56 | - $this->beta = $beta; |
|
57 | - $this->wp_override = $wp_override; |
|
58 | - $this->plugin_slug = $this->removePluginSlugPrefixes($plugin_slug); |
|
59 | - $this->min_core_version = $min_core_version; |
|
60 | - |
|
61 | - add_action('init', [$this, 'loadPluginUpdater']); |
|
62 | - add_action( |
|
63 | - 'AHEE__EventEspresso_core_services_licensing_PluginLicenseCollection__loadPluginLicenses', |
|
64 | - [$this, 'loadPluginLicense'] |
|
65 | - ); |
|
66 | - add_filter( |
|
67 | - 'FHEE__LicenseKeysAdminForm__generate__form_subsections', |
|
68 | - [$this, 'addLicenseKeyFormSection'] |
|
69 | - ); |
|
70 | - add_filter( |
|
71 | - 'edd_sl_plugin_updater_api_params', |
|
72 | - [$this, 'addCustomApiParams'], |
|
73 | - 10, |
|
74 | - 2 |
|
75 | - ); |
|
76 | - } |
|
77 | - |
|
78 | - |
|
79 | - /** |
|
80 | - * removes unnecessary prefixes from plugin slugs |
|
81 | - * |
|
82 | - * @param string $plugin_slug |
|
83 | - * @return string |
|
84 | - */ |
|
85 | - private function removePluginSlugPrefixes(string $plugin_slug): string |
|
86 | - { |
|
87 | - foreach (self::$slug_prefixes as $slug_prefix) { |
|
88 | - if (strpos($plugin_slug, $slug_prefix) === 0) { |
|
89 | - $plugin_slug = str_replace($slug_prefix, '', $plugin_slug); |
|
90 | - } |
|
91 | - } |
|
92 | - return $plugin_slug; |
|
93 | - } |
|
94 | - |
|
95 | - |
|
96 | - /** |
|
97 | - * @return PluginUpdater|null |
|
98 | - */ |
|
99 | - public function updater(): ?PluginUpdater |
|
100 | - { |
|
101 | - return $this->updater; |
|
102 | - } |
|
103 | - |
|
104 | - |
|
105 | - /** |
|
106 | - * @return string |
|
107 | - */ |
|
108 | - public function itemID(): string |
|
109 | - { |
|
110 | - return $this->plugin_id; |
|
111 | - } |
|
112 | - |
|
113 | - |
|
114 | - /** |
|
115 | - * @return string |
|
116 | - */ |
|
117 | - public function itemName(): string |
|
118 | - { |
|
119 | - return $this->plugin_name; |
|
120 | - } |
|
121 | - |
|
122 | - |
|
123 | - private function getLicenseKeyData(): stdClass |
|
124 | - { |
|
125 | - if (! $this->license_key_data instanceof LicenseKeyData) { |
|
126 | - $this->license_key_data = LoaderFactory::getShared(LicenseKeyData::class); |
|
127 | - } |
|
128 | - return $this->license_key_data->getLicenseDataForPlugin($this->pluginSlug()); |
|
129 | - } |
|
130 | - |
|
131 | - |
|
132 | - /** |
|
133 | - * @return string |
|
134 | - */ |
|
135 | - public function licenseKey(): string |
|
136 | - { |
|
137 | - return $this->license_key; |
|
138 | - } |
|
139 | - |
|
140 | - |
|
141 | - /** |
|
142 | - * @param string $license_key |
|
143 | - */ |
|
144 | - public function updateLicenseKey(string $license_key): void |
|
145 | - { |
|
146 | - $this->license_key = sanitize_text_field($license_key); |
|
147 | - } |
|
148 | - |
|
149 | - |
|
150 | - /** |
|
151 | - * @return string |
|
152 | - */ |
|
153 | - public function mainfile(): string |
|
154 | - { |
|
155 | - return $this->mainfile; |
|
156 | - } |
|
157 | - |
|
158 | - |
|
159 | - /** |
|
160 | - * @return string |
|
161 | - * @since 5.0.20.p |
|
162 | - */ |
|
163 | - public function minCoreVersion(): string |
|
164 | - { |
|
165 | - return $this->min_core_version; |
|
166 | - } |
|
167 | - |
|
168 | - |
|
169 | - /** |
|
170 | - * @return string |
|
171 | - */ |
|
172 | - public function pluginSlug(): string |
|
173 | - { |
|
174 | - return $this->plugin_slug; |
|
175 | - } |
|
176 | - |
|
177 | - |
|
178 | - /** |
|
179 | - * @return string |
|
180 | - */ |
|
181 | - public function version(): string |
|
182 | - { |
|
183 | - return $this->version; |
|
184 | - } |
|
185 | - |
|
186 | - |
|
187 | - /** |
|
188 | - * @return bool |
|
189 | - */ |
|
190 | - public function isBeta(): bool |
|
191 | - { |
|
192 | - return $this->beta; |
|
193 | - } |
|
194 | - |
|
195 | - |
|
196 | - /** |
|
197 | - * @return bool |
|
198 | - */ |
|
199 | - public function isWpOverride(): bool |
|
200 | - { |
|
201 | - return $this->wp_override; |
|
202 | - } |
|
203 | - |
|
204 | - |
|
205 | - public function loadPluginUpdater() |
|
206 | - { |
|
207 | - // To support auto-updates, this needs to run during the wp_version_check cron job for privileged users. |
|
208 | - if (! current_user_can('manage_options') && ! (defined('DOING_CRON') && DOING_CRON)) { |
|
209 | - return; |
|
210 | - } |
|
211 | - |
|
212 | - $license_key_data = $this->getLicenseKeyData(); |
|
213 | - $license_key = $license_key_data->license_key ?? ''; |
|
214 | - if ($license_key) { |
|
215 | - $this->updateLicenseKey($license_key); |
|
216 | - } |
|
217 | - |
|
218 | - $this->updater = new PluginUpdater( |
|
219 | - LicenseAPI::url(), |
|
220 | - $this->mainfile(), |
|
221 | - [ |
|
222 | - 'author' => LicenseAPI::AUTHOR, |
|
223 | - 'beta' => $this->isBeta(), |
|
224 | - 'item_id' => $this->itemID(), |
|
225 | - 'item_name' => $this->itemName(), |
|
226 | - 'license' => $this->licenseKey(), |
|
227 | - 'version' => $this->version(), |
|
228 | - 'wp_override' => $this->isWpOverride(), |
|
229 | - ] |
|
230 | - ); |
|
231 | - } |
|
232 | - |
|
233 | - |
|
234 | - public function loadPluginLicense(PluginLicenseCollection $plugin_license_collection) |
|
235 | - { |
|
236 | - $plugin_license_collection->add($this, $this->pluginSlug()); |
|
237 | - } |
|
238 | - |
|
239 | - |
|
240 | - public function addLicenseKeyFormSection(array $license_keys_form_subsections): array |
|
241 | - { |
|
242 | - if (is_main_site()) { |
|
243 | - $license_keys_form_subsections[ $this->pluginSlug() ] = LoaderFactory::getNew( |
|
244 | - LicenseKeyFormInput::class, |
|
245 | - [$this] |
|
246 | - ); |
|
247 | - } |
|
248 | - return $license_keys_form_subsections; |
|
249 | - } |
|
250 | - |
|
251 | - |
|
252 | - public function addCustomApiParams($api_params, $api_data) |
|
253 | - { |
|
254 | - if ($api_data['item_name'] === $this->itemName()) { |
|
255 | - $api_params['event_espresso_core_version'] = EVENT_ESPRESSO_VERSION; |
|
256 | - } |
|
257 | - return $api_params; |
|
258 | - } |
|
17 | + private ?LicenseKeyData $license_key_data = null; |
|
18 | + |
|
19 | + private ?PluginUpdater $updater = null; |
|
20 | + |
|
21 | + private string $license_key = ''; |
|
22 | + |
|
23 | + private string $mainfile; |
|
24 | + |
|
25 | + private string $min_core_version; |
|
26 | + |
|
27 | + private string $plugin_id; |
|
28 | + |
|
29 | + private string $plugin_name; |
|
30 | + |
|
31 | + private string $plugin_slug; |
|
32 | + |
|
33 | + private string $version; |
|
34 | + |
|
35 | + private bool $beta; |
|
36 | + |
|
37 | + private bool $wp_override; |
|
38 | + |
|
39 | + private static array $slug_prefixes = ['ee-', 'eea-', 'eep-', 'espresso-', 'ee_', 'eea_', 'eep_', 'espresso_']; |
|
40 | + |
|
41 | + |
|
42 | + public function __construct( |
|
43 | + string $mainfile, |
|
44 | + string $plugin_id, |
|
45 | + string $plugin_name, |
|
46 | + string $plugin_slug, |
|
47 | + string $version, |
|
48 | + bool $beta = false, |
|
49 | + bool $wp_override = false, |
|
50 | + string $min_core_version = '' |
|
51 | + ) { |
|
52 | + $this->mainfile = $mainfile; |
|
53 | + $this->plugin_id = $plugin_id; |
|
54 | + $this->plugin_name = $plugin_name; |
|
55 | + $this->version = $version; |
|
56 | + $this->beta = $beta; |
|
57 | + $this->wp_override = $wp_override; |
|
58 | + $this->plugin_slug = $this->removePluginSlugPrefixes($plugin_slug); |
|
59 | + $this->min_core_version = $min_core_version; |
|
60 | + |
|
61 | + add_action('init', [$this, 'loadPluginUpdater']); |
|
62 | + add_action( |
|
63 | + 'AHEE__EventEspresso_core_services_licensing_PluginLicenseCollection__loadPluginLicenses', |
|
64 | + [$this, 'loadPluginLicense'] |
|
65 | + ); |
|
66 | + add_filter( |
|
67 | + 'FHEE__LicenseKeysAdminForm__generate__form_subsections', |
|
68 | + [$this, 'addLicenseKeyFormSection'] |
|
69 | + ); |
|
70 | + add_filter( |
|
71 | + 'edd_sl_plugin_updater_api_params', |
|
72 | + [$this, 'addCustomApiParams'], |
|
73 | + 10, |
|
74 | + 2 |
|
75 | + ); |
|
76 | + } |
|
77 | + |
|
78 | + |
|
79 | + /** |
|
80 | + * removes unnecessary prefixes from plugin slugs |
|
81 | + * |
|
82 | + * @param string $plugin_slug |
|
83 | + * @return string |
|
84 | + */ |
|
85 | + private function removePluginSlugPrefixes(string $plugin_slug): string |
|
86 | + { |
|
87 | + foreach (self::$slug_prefixes as $slug_prefix) { |
|
88 | + if (strpos($plugin_slug, $slug_prefix) === 0) { |
|
89 | + $plugin_slug = str_replace($slug_prefix, '', $plugin_slug); |
|
90 | + } |
|
91 | + } |
|
92 | + return $plugin_slug; |
|
93 | + } |
|
94 | + |
|
95 | + |
|
96 | + /** |
|
97 | + * @return PluginUpdater|null |
|
98 | + */ |
|
99 | + public function updater(): ?PluginUpdater |
|
100 | + { |
|
101 | + return $this->updater; |
|
102 | + } |
|
103 | + |
|
104 | + |
|
105 | + /** |
|
106 | + * @return string |
|
107 | + */ |
|
108 | + public function itemID(): string |
|
109 | + { |
|
110 | + return $this->plugin_id; |
|
111 | + } |
|
112 | + |
|
113 | + |
|
114 | + /** |
|
115 | + * @return string |
|
116 | + */ |
|
117 | + public function itemName(): string |
|
118 | + { |
|
119 | + return $this->plugin_name; |
|
120 | + } |
|
121 | + |
|
122 | + |
|
123 | + private function getLicenseKeyData(): stdClass |
|
124 | + { |
|
125 | + if (! $this->license_key_data instanceof LicenseKeyData) { |
|
126 | + $this->license_key_data = LoaderFactory::getShared(LicenseKeyData::class); |
|
127 | + } |
|
128 | + return $this->license_key_data->getLicenseDataForPlugin($this->pluginSlug()); |
|
129 | + } |
|
130 | + |
|
131 | + |
|
132 | + /** |
|
133 | + * @return string |
|
134 | + */ |
|
135 | + public function licenseKey(): string |
|
136 | + { |
|
137 | + return $this->license_key; |
|
138 | + } |
|
139 | + |
|
140 | + |
|
141 | + /** |
|
142 | + * @param string $license_key |
|
143 | + */ |
|
144 | + public function updateLicenseKey(string $license_key): void |
|
145 | + { |
|
146 | + $this->license_key = sanitize_text_field($license_key); |
|
147 | + } |
|
148 | + |
|
149 | + |
|
150 | + /** |
|
151 | + * @return string |
|
152 | + */ |
|
153 | + public function mainfile(): string |
|
154 | + { |
|
155 | + return $this->mainfile; |
|
156 | + } |
|
157 | + |
|
158 | + |
|
159 | + /** |
|
160 | + * @return string |
|
161 | + * @since 5.0.20.p |
|
162 | + */ |
|
163 | + public function minCoreVersion(): string |
|
164 | + { |
|
165 | + return $this->min_core_version; |
|
166 | + } |
|
167 | + |
|
168 | + |
|
169 | + /** |
|
170 | + * @return string |
|
171 | + */ |
|
172 | + public function pluginSlug(): string |
|
173 | + { |
|
174 | + return $this->plugin_slug; |
|
175 | + } |
|
176 | + |
|
177 | + |
|
178 | + /** |
|
179 | + * @return string |
|
180 | + */ |
|
181 | + public function version(): string |
|
182 | + { |
|
183 | + return $this->version; |
|
184 | + } |
|
185 | + |
|
186 | + |
|
187 | + /** |
|
188 | + * @return bool |
|
189 | + */ |
|
190 | + public function isBeta(): bool |
|
191 | + { |
|
192 | + return $this->beta; |
|
193 | + } |
|
194 | + |
|
195 | + |
|
196 | + /** |
|
197 | + * @return bool |
|
198 | + */ |
|
199 | + public function isWpOverride(): bool |
|
200 | + { |
|
201 | + return $this->wp_override; |
|
202 | + } |
|
203 | + |
|
204 | + |
|
205 | + public function loadPluginUpdater() |
|
206 | + { |
|
207 | + // To support auto-updates, this needs to run during the wp_version_check cron job for privileged users. |
|
208 | + if (! current_user_can('manage_options') && ! (defined('DOING_CRON') && DOING_CRON)) { |
|
209 | + return; |
|
210 | + } |
|
211 | + |
|
212 | + $license_key_data = $this->getLicenseKeyData(); |
|
213 | + $license_key = $license_key_data->license_key ?? ''; |
|
214 | + if ($license_key) { |
|
215 | + $this->updateLicenseKey($license_key); |
|
216 | + } |
|
217 | + |
|
218 | + $this->updater = new PluginUpdater( |
|
219 | + LicenseAPI::url(), |
|
220 | + $this->mainfile(), |
|
221 | + [ |
|
222 | + 'author' => LicenseAPI::AUTHOR, |
|
223 | + 'beta' => $this->isBeta(), |
|
224 | + 'item_id' => $this->itemID(), |
|
225 | + 'item_name' => $this->itemName(), |
|
226 | + 'license' => $this->licenseKey(), |
|
227 | + 'version' => $this->version(), |
|
228 | + 'wp_override' => $this->isWpOverride(), |
|
229 | + ] |
|
230 | + ); |
|
231 | + } |
|
232 | + |
|
233 | + |
|
234 | + public function loadPluginLicense(PluginLicenseCollection $plugin_license_collection) |
|
235 | + { |
|
236 | + $plugin_license_collection->add($this, $this->pluginSlug()); |
|
237 | + } |
|
238 | + |
|
239 | + |
|
240 | + public function addLicenseKeyFormSection(array $license_keys_form_subsections): array |
|
241 | + { |
|
242 | + if (is_main_site()) { |
|
243 | + $license_keys_form_subsections[ $this->pluginSlug() ] = LoaderFactory::getNew( |
|
244 | + LicenseKeyFormInput::class, |
|
245 | + [$this] |
|
246 | + ); |
|
247 | + } |
|
248 | + return $license_keys_form_subsections; |
|
249 | + } |
|
250 | + |
|
251 | + |
|
252 | + public function addCustomApiParams($api_params, $api_data) |
|
253 | + { |
|
254 | + if ($api_data['item_name'] === $this->itemName()) { |
|
255 | + $api_params['event_espresso_core_version'] = EVENT_ESPRESSO_VERSION; |
|
256 | + } |
|
257 | + return $api_params; |
|
258 | + } |
|
259 | 259 | } |
@@ -122,7 +122,7 @@ discard block |
||
122 | 122 | |
123 | 123 | private function getLicenseKeyData(): stdClass |
124 | 124 | { |
125 | - if (! $this->license_key_data instanceof LicenseKeyData) { |
|
125 | + if ( ! $this->license_key_data instanceof LicenseKeyData) { |
|
126 | 126 | $this->license_key_data = LoaderFactory::getShared(LicenseKeyData::class); |
127 | 127 | } |
128 | 128 | return $this->license_key_data->getLicenseDataForPlugin($this->pluginSlug()); |
@@ -205,7 +205,7 @@ discard block |
||
205 | 205 | public function loadPluginUpdater() |
206 | 206 | { |
207 | 207 | // To support auto-updates, this needs to run during the wp_version_check cron job for privileged users. |
208 | - if (! current_user_can('manage_options') && ! (defined('DOING_CRON') && DOING_CRON)) { |
|
208 | + if ( ! current_user_can('manage_options') && ! (defined('DOING_CRON') && DOING_CRON)) { |
|
209 | 209 | return; |
210 | 210 | } |
211 | 211 | |
@@ -240,7 +240,7 @@ discard block |
||
240 | 240 | public function addLicenseKeyFormSection(array $license_keys_form_subsections): array |
241 | 241 | { |
242 | 242 | if (is_main_site()) { |
243 | - $license_keys_form_subsections[ $this->pluginSlug() ] = LoaderFactory::getNew( |
|
243 | + $license_keys_form_subsections[$this->pluginSlug()] = LoaderFactory::getNew( |
|
244 | 244 | LicenseKeyFormInput::class, |
245 | 245 | [$this] |
246 | 246 | ); |
@@ -6,117 +6,117 @@ |
||
6 | 6 | |
7 | 7 | class LicenseManager |
8 | 8 | { |
9 | - private LicenseAPI $license_api; |
|
10 | - |
|
11 | - private LicenseKeyData $license_key_data; |
|
12 | - |
|
13 | - private PluginLicenseCollection $plugin_license_collection; |
|
14 | - |
|
15 | - |
|
16 | - public function __construct( |
|
17 | - LicenseAPI $license_api, |
|
18 | - LicenseKeyData $license_key_data, |
|
19 | - PluginLicenseCollection $plugin_license_collection |
|
20 | - ) { |
|
21 | - $this->license_api = $license_api; |
|
22 | - $this->license_key_data = $license_key_data; |
|
23 | - $this->plugin_license_collection = $plugin_license_collection; |
|
24 | - } |
|
25 | - |
|
26 | - |
|
27 | - public function activateLicense( |
|
28 | - string $license_key, |
|
29 | - string $item_id, |
|
30 | - string $item_name, |
|
31 | - string $plugin_slug, |
|
32 | - string $plugin_version, |
|
33 | - string $min_core_version = '' |
|
34 | - ): stdCLass { |
|
35 | - $license_data = $this->license_api->postRequest( |
|
36 | - LicenseAPI::ACTION_ACTIVATE, |
|
37 | - $license_key, |
|
38 | - $item_id, |
|
39 | - $item_name, |
|
40 | - $plugin_version, |
|
41 | - $min_core_version |
|
42 | - ); |
|
43 | - $license_data->license_key = $license_data->license === 'valid' ? $license_key : ''; |
|
44 | - $this->license_key_data->updateLicenseDataForPlugin($license_data, $plugin_slug); |
|
45 | - return $license_data; |
|
46 | - } |
|
47 | - |
|
48 | - |
|
49 | - public function deactivateLicense( |
|
50 | - string $license_key, |
|
51 | - string $item_id, |
|
52 | - string $item_name, |
|
53 | - string $plugin_slug, |
|
54 | - string $plugin_version, |
|
55 | - string $min_core_version = '' |
|
56 | - ): stdCLass { |
|
57 | - $license_data = $this->license_api->postRequest( |
|
58 | - LicenseAPI::ACTION_DEACTIVATE, |
|
59 | - $license_key, |
|
60 | - $item_id, |
|
61 | - $item_name, |
|
62 | - $plugin_version, |
|
63 | - $min_core_version |
|
64 | - ); |
|
65 | - $this->license_key_data->removeLicenseDataForPlugin($plugin_slug); |
|
66 | - return $license_data; |
|
67 | - } |
|
68 | - |
|
69 | - |
|
70 | - public function resetLicenseKey(string $plugin_slug): stdCLass |
|
71 | - { |
|
72 | - $license_data = $this->getLicenseData($plugin_slug); |
|
73 | - $license_data->license_key = ''; |
|
74 | - $this->license_key_data->updateLicenseDataForPlugin($license_data, $plugin_slug, true); |
|
75 | - return $license_data; |
|
76 | - } |
|
77 | - |
|
78 | - |
|
79 | - public function checkLicense( |
|
80 | - string $license_key, |
|
81 | - string $item_id, |
|
82 | - string $item_name, |
|
83 | - string $plugin_slug, |
|
84 | - string $plugin_version, |
|
85 | - string $min_core_version = '' |
|
86 | - ): stdCLass { |
|
87 | - $existing_license_data = $this->getLicenseData($plugin_slug); |
|
88 | - $license_data = $this->license_api->postRequest( |
|
89 | - LicenseAPI::ACTION_CHECK, |
|
90 | - $license_key, |
|
91 | - $item_id, |
|
92 | - $item_name, |
|
93 | - $plugin_version, |
|
94 | - $min_core_version |
|
95 | - ); |
|
96 | - $license_data = (array) $license_data + (array) $existing_license_data; |
|
97 | - return (object) $license_data; |
|
98 | - } |
|
99 | - |
|
100 | - |
|
101 | - public function getLicenseData(string $plugin_slug): stdClass |
|
102 | - { |
|
103 | - return $this->license_key_data->getLicenseDataForPlugin($plugin_slug); |
|
104 | - } |
|
105 | - |
|
106 | - |
|
107 | - public function getVersionInfo(): stdClass |
|
108 | - { |
|
109 | - $products = []; |
|
110 | - foreach ($this->plugin_license_collection as $plugin_license) { |
|
111 | - if (! $plugin_license instanceof PluginLicense) { |
|
112 | - continue; |
|
113 | - } |
|
114 | - $products[ $plugin_license->pluginSlug() ] = [ |
|
115 | - 'item_id' => $plugin_license->itemId(), |
|
116 | - 'license' => $plugin_license->licenseKey(), |
|
117 | - 'url' => LicenseAPI::url(), |
|
118 | - ]; |
|
119 | - } |
|
120 | - return $this->license_api->getProductVersions($products); |
|
121 | - } |
|
9 | + private LicenseAPI $license_api; |
|
10 | + |
|
11 | + private LicenseKeyData $license_key_data; |
|
12 | + |
|
13 | + private PluginLicenseCollection $plugin_license_collection; |
|
14 | + |
|
15 | + |
|
16 | + public function __construct( |
|
17 | + LicenseAPI $license_api, |
|
18 | + LicenseKeyData $license_key_data, |
|
19 | + PluginLicenseCollection $plugin_license_collection |
|
20 | + ) { |
|
21 | + $this->license_api = $license_api; |
|
22 | + $this->license_key_data = $license_key_data; |
|
23 | + $this->plugin_license_collection = $plugin_license_collection; |
|
24 | + } |
|
25 | + |
|
26 | + |
|
27 | + public function activateLicense( |
|
28 | + string $license_key, |
|
29 | + string $item_id, |
|
30 | + string $item_name, |
|
31 | + string $plugin_slug, |
|
32 | + string $plugin_version, |
|
33 | + string $min_core_version = '' |
|
34 | + ): stdCLass { |
|
35 | + $license_data = $this->license_api->postRequest( |
|
36 | + LicenseAPI::ACTION_ACTIVATE, |
|
37 | + $license_key, |
|
38 | + $item_id, |
|
39 | + $item_name, |
|
40 | + $plugin_version, |
|
41 | + $min_core_version |
|
42 | + ); |
|
43 | + $license_data->license_key = $license_data->license === 'valid' ? $license_key : ''; |
|
44 | + $this->license_key_data->updateLicenseDataForPlugin($license_data, $plugin_slug); |
|
45 | + return $license_data; |
|
46 | + } |
|
47 | + |
|
48 | + |
|
49 | + public function deactivateLicense( |
|
50 | + string $license_key, |
|
51 | + string $item_id, |
|
52 | + string $item_name, |
|
53 | + string $plugin_slug, |
|
54 | + string $plugin_version, |
|
55 | + string $min_core_version = '' |
|
56 | + ): stdCLass { |
|
57 | + $license_data = $this->license_api->postRequest( |
|
58 | + LicenseAPI::ACTION_DEACTIVATE, |
|
59 | + $license_key, |
|
60 | + $item_id, |
|
61 | + $item_name, |
|
62 | + $plugin_version, |
|
63 | + $min_core_version |
|
64 | + ); |
|
65 | + $this->license_key_data->removeLicenseDataForPlugin($plugin_slug); |
|
66 | + return $license_data; |
|
67 | + } |
|
68 | + |
|
69 | + |
|
70 | + public function resetLicenseKey(string $plugin_slug): stdCLass |
|
71 | + { |
|
72 | + $license_data = $this->getLicenseData($plugin_slug); |
|
73 | + $license_data->license_key = ''; |
|
74 | + $this->license_key_data->updateLicenseDataForPlugin($license_data, $plugin_slug, true); |
|
75 | + return $license_data; |
|
76 | + } |
|
77 | + |
|
78 | + |
|
79 | + public function checkLicense( |
|
80 | + string $license_key, |
|
81 | + string $item_id, |
|
82 | + string $item_name, |
|
83 | + string $plugin_slug, |
|
84 | + string $plugin_version, |
|
85 | + string $min_core_version = '' |
|
86 | + ): stdCLass { |
|
87 | + $existing_license_data = $this->getLicenseData($plugin_slug); |
|
88 | + $license_data = $this->license_api->postRequest( |
|
89 | + LicenseAPI::ACTION_CHECK, |
|
90 | + $license_key, |
|
91 | + $item_id, |
|
92 | + $item_name, |
|
93 | + $plugin_version, |
|
94 | + $min_core_version |
|
95 | + ); |
|
96 | + $license_data = (array) $license_data + (array) $existing_license_data; |
|
97 | + return (object) $license_data; |
|
98 | + } |
|
99 | + |
|
100 | + |
|
101 | + public function getLicenseData(string $plugin_slug): stdClass |
|
102 | + { |
|
103 | + return $this->license_key_data->getLicenseDataForPlugin($plugin_slug); |
|
104 | + } |
|
105 | + |
|
106 | + |
|
107 | + public function getVersionInfo(): stdClass |
|
108 | + { |
|
109 | + $products = []; |
|
110 | + foreach ($this->plugin_license_collection as $plugin_license) { |
|
111 | + if (! $plugin_license instanceof PluginLicense) { |
|
112 | + continue; |
|
113 | + } |
|
114 | + $products[ $plugin_license->pluginSlug() ] = [ |
|
115 | + 'item_id' => $plugin_license->itemId(), |
|
116 | + 'license' => $plugin_license->licenseKey(), |
|
117 | + 'url' => LicenseAPI::url(), |
|
118 | + ]; |
|
119 | + } |
|
120 | + return $this->license_api->getProductVersions($products); |
|
121 | + } |
|
122 | 122 | } |
@@ -17,176 +17,176 @@ |
||
17 | 17 | */ |
18 | 18 | abstract class WordPressOption |
19 | 19 | { |
20 | - public const NOT_SET_YET = 'wordpress-option-value-not-yet-set'; |
|
21 | - |
|
22 | - /** |
|
23 | - * WordPress makes it difficult to determine if an option successfully saved or not, |
|
24 | - * which is sometimes really important to know, especially if the information you are saving is critical. |
|
25 | - * The following options allow us to have a better chance of knowing when an update actually failed |
|
26 | - * or when everything is OK but it just didn't update because the value hasn't changed. |
|
27 | - */ |
|
28 | - public const UPDATE_SUCCESS = 1; |
|
29 | - |
|
30 | - public const UPDATE_NONE = 0; |
|
31 | - |
|
32 | - public const UPDATE_ERROR = -1; |
|
33 | - |
|
34 | - private bool $autoload = false; |
|
35 | - |
|
36 | - /** |
|
37 | - * @var mixed |
|
38 | - */ |
|
39 | - private $default_value = null; |
|
40 | - |
|
41 | - private string $option_name = ''; |
|
42 | - |
|
43 | - /** |
|
44 | - * @var mixed |
|
45 | - */ |
|
46 | - private $value = WordPressOption::NOT_SET_YET; |
|
47 | - |
|
48 | - private OptionEngine $option_engine; |
|
49 | - |
|
50 | - |
|
51 | - /** |
|
52 | - * WordPressOption constructor. |
|
53 | - * |
|
54 | - * @param string $option_name |
|
55 | - * @param mixed $default_value |
|
56 | - * @param bool $autoload if true, will load the option on EVERY request |
|
57 | - * @param bool $is_network_option if true, will save the option to the network as opposed to the current blog |
|
58 | - */ |
|
59 | - public function __construct( |
|
60 | - string $option_name, |
|
61 | - $default_value, |
|
62 | - bool $autoload = false, |
|
63 | - bool $is_network_option = false |
|
64 | - ) { |
|
65 | - $this->setAutoload($autoload); |
|
66 | - $this->setDefaultValue($default_value); |
|
67 | - $this->setOptionName($option_name); |
|
68 | - $this->option_engine = new OptionEngine($is_network_option); |
|
69 | - } |
|
70 | - |
|
71 | - |
|
72 | - /** |
|
73 | - * @param bool|string $autoload |
|
74 | - */ |
|
75 | - public function setAutoload($autoload): void |
|
76 | - { |
|
77 | - $this->autoload = filter_var($autoload, FILTER_VALIDATE_BOOLEAN); |
|
78 | - } |
|
79 | - |
|
80 | - |
|
81 | - /** |
|
82 | - * @param mixed $default_value |
|
83 | - */ |
|
84 | - public function setDefaultValue($default_value): void |
|
85 | - { |
|
86 | - $this->default_value = $default_value; |
|
87 | - } |
|
88 | - |
|
89 | - |
|
90 | - /** |
|
91 | - * @param string $option_name |
|
92 | - */ |
|
93 | - public function setOptionName(string $option_name): void |
|
94 | - { |
|
95 | - $this->option_name = sanitize_key($option_name); |
|
96 | - } |
|
97 | - |
|
98 | - |
|
99 | - /** |
|
100 | - * @return string |
|
101 | - */ |
|
102 | - public function optionExists(): string |
|
103 | - { |
|
104 | - return $this->option_engine->getOption( |
|
105 | - $this->getOptionName(), |
|
106 | - WordPressOption::NOT_SET_YET |
|
107 | - ) !== WordPressOption::NOT_SET_YET; |
|
108 | - } |
|
109 | - |
|
110 | - |
|
111 | - /** |
|
112 | - * @return string |
|
113 | - */ |
|
114 | - public function getOptionName(): string |
|
115 | - { |
|
116 | - return $this->option_name; |
|
117 | - } |
|
118 | - |
|
119 | - |
|
120 | - /** |
|
121 | - * @return false|mixed|void |
|
122 | - */ |
|
123 | - public function loadOption() |
|
124 | - { |
|
125 | - if ($this->value === WordPressOption::NOT_SET_YET) { |
|
126 | - $this->value = $this->option_engine->getOption($this->getOptionName(), $this->default_value); |
|
127 | - } |
|
128 | - return $this->value; |
|
129 | - } |
|
130 | - |
|
131 | - |
|
132 | - /** |
|
133 | - * @param $value |
|
134 | - * @param bool $force_update |
|
135 | - * @return int |
|
136 | - */ |
|
137 | - public function updateOption($value, bool $force_update = false): int |
|
138 | - { |
|
139 | - // don't update if value has not changed since last update |
|
140 | - if (! $force_update && $this->valueIsUnchanged($value)) { |
|
141 | - return WordPressOption::UPDATE_NONE; |
|
142 | - } |
|
143 | - if ($force_update) { |
|
144 | - $this->option_engine->updateOption($this->getOptionName(), null); |
|
145 | - } |
|
146 | - $this->value = $value; |
|
147 | - // because the options for updating differ when adding an option for the first time |
|
148 | - // we use the WordPressOption::NOT_SET_YET to determine if things already exist in the db |
|
149 | - $updated = $this->optionExists() |
|
150 | - ? $this->option_engine->updateOption($this->getOptionName(), $this->value) |
|
151 | - : $this->option_engine->addOption($this->getOptionName(), $this->value, $this->autoload()); |
|
152 | - |
|
153 | - if ($updated) { |
|
154 | - return WordPressOption::UPDATE_SUCCESS; |
|
155 | - } |
|
156 | - return WordPressOption::UPDATE_ERROR; |
|
157 | - } |
|
158 | - |
|
159 | - |
|
160 | - private function valueIsUnchanged($value): bool |
|
161 | - { |
|
162 | - if (is_array($value) && is_array($this->value)) { |
|
163 | - $diff = EEH_Array::array_diff_recursive($value, $this->value); |
|
164 | - // $diff = array_diff($value, $this->value); |
|
165 | - return empty($diff); |
|
166 | - } |
|
167 | - // emulate WP's method for checking equality |
|
168 | - return $value === $this->value && maybe_serialize($value) === maybe_serialize($this->value); |
|
169 | - } |
|
170 | - |
|
171 | - |
|
172 | - /** |
|
173 | - * @return string |
|
174 | - */ |
|
175 | - private function autoload(): string |
|
176 | - { |
|
177 | - return $this->autoload ? 'yes' : 'no'; |
|
178 | - } |
|
179 | - |
|
180 | - |
|
181 | - /** |
|
182 | - * Deletes the option from the database |
|
183 | - * for the rest of the request |
|
184 | - * |
|
185 | - * @return bool |
|
186 | - * @since 5.0.0.p |
|
187 | - */ |
|
188 | - public function deleteOption(): bool |
|
189 | - { |
|
190 | - return $this->option_engine->deleteOption($this->getOptionName()); |
|
191 | - } |
|
20 | + public const NOT_SET_YET = 'wordpress-option-value-not-yet-set'; |
|
21 | + |
|
22 | + /** |
|
23 | + * WordPress makes it difficult to determine if an option successfully saved or not, |
|
24 | + * which is sometimes really important to know, especially if the information you are saving is critical. |
|
25 | + * The following options allow us to have a better chance of knowing when an update actually failed |
|
26 | + * or when everything is OK but it just didn't update because the value hasn't changed. |
|
27 | + */ |
|
28 | + public const UPDATE_SUCCESS = 1; |
|
29 | + |
|
30 | + public const UPDATE_NONE = 0; |
|
31 | + |
|
32 | + public const UPDATE_ERROR = -1; |
|
33 | + |
|
34 | + private bool $autoload = false; |
|
35 | + |
|
36 | + /** |
|
37 | + * @var mixed |
|
38 | + */ |
|
39 | + private $default_value = null; |
|
40 | + |
|
41 | + private string $option_name = ''; |
|
42 | + |
|
43 | + /** |
|
44 | + * @var mixed |
|
45 | + */ |
|
46 | + private $value = WordPressOption::NOT_SET_YET; |
|
47 | + |
|
48 | + private OptionEngine $option_engine; |
|
49 | + |
|
50 | + |
|
51 | + /** |
|
52 | + * WordPressOption constructor. |
|
53 | + * |
|
54 | + * @param string $option_name |
|
55 | + * @param mixed $default_value |
|
56 | + * @param bool $autoload if true, will load the option on EVERY request |
|
57 | + * @param bool $is_network_option if true, will save the option to the network as opposed to the current blog |
|
58 | + */ |
|
59 | + public function __construct( |
|
60 | + string $option_name, |
|
61 | + $default_value, |
|
62 | + bool $autoload = false, |
|
63 | + bool $is_network_option = false |
|
64 | + ) { |
|
65 | + $this->setAutoload($autoload); |
|
66 | + $this->setDefaultValue($default_value); |
|
67 | + $this->setOptionName($option_name); |
|
68 | + $this->option_engine = new OptionEngine($is_network_option); |
|
69 | + } |
|
70 | + |
|
71 | + |
|
72 | + /** |
|
73 | + * @param bool|string $autoload |
|
74 | + */ |
|
75 | + public function setAutoload($autoload): void |
|
76 | + { |
|
77 | + $this->autoload = filter_var($autoload, FILTER_VALIDATE_BOOLEAN); |
|
78 | + } |
|
79 | + |
|
80 | + |
|
81 | + /** |
|
82 | + * @param mixed $default_value |
|
83 | + */ |
|
84 | + public function setDefaultValue($default_value): void |
|
85 | + { |
|
86 | + $this->default_value = $default_value; |
|
87 | + } |
|
88 | + |
|
89 | + |
|
90 | + /** |
|
91 | + * @param string $option_name |
|
92 | + */ |
|
93 | + public function setOptionName(string $option_name): void |
|
94 | + { |
|
95 | + $this->option_name = sanitize_key($option_name); |
|
96 | + } |
|
97 | + |
|
98 | + |
|
99 | + /** |
|
100 | + * @return string |
|
101 | + */ |
|
102 | + public function optionExists(): string |
|
103 | + { |
|
104 | + return $this->option_engine->getOption( |
|
105 | + $this->getOptionName(), |
|
106 | + WordPressOption::NOT_SET_YET |
|
107 | + ) !== WordPressOption::NOT_SET_YET; |
|
108 | + } |
|
109 | + |
|
110 | + |
|
111 | + /** |
|
112 | + * @return string |
|
113 | + */ |
|
114 | + public function getOptionName(): string |
|
115 | + { |
|
116 | + return $this->option_name; |
|
117 | + } |
|
118 | + |
|
119 | + |
|
120 | + /** |
|
121 | + * @return false|mixed|void |
|
122 | + */ |
|
123 | + public function loadOption() |
|
124 | + { |
|
125 | + if ($this->value === WordPressOption::NOT_SET_YET) { |
|
126 | + $this->value = $this->option_engine->getOption($this->getOptionName(), $this->default_value); |
|
127 | + } |
|
128 | + return $this->value; |
|
129 | + } |
|
130 | + |
|
131 | + |
|
132 | + /** |
|
133 | + * @param $value |
|
134 | + * @param bool $force_update |
|
135 | + * @return int |
|
136 | + */ |
|
137 | + public function updateOption($value, bool $force_update = false): int |
|
138 | + { |
|
139 | + // don't update if value has not changed since last update |
|
140 | + if (! $force_update && $this->valueIsUnchanged($value)) { |
|
141 | + return WordPressOption::UPDATE_NONE; |
|
142 | + } |
|
143 | + if ($force_update) { |
|
144 | + $this->option_engine->updateOption($this->getOptionName(), null); |
|
145 | + } |
|
146 | + $this->value = $value; |
|
147 | + // because the options for updating differ when adding an option for the first time |
|
148 | + // we use the WordPressOption::NOT_SET_YET to determine if things already exist in the db |
|
149 | + $updated = $this->optionExists() |
|
150 | + ? $this->option_engine->updateOption($this->getOptionName(), $this->value) |
|
151 | + : $this->option_engine->addOption($this->getOptionName(), $this->value, $this->autoload()); |
|
152 | + |
|
153 | + if ($updated) { |
|
154 | + return WordPressOption::UPDATE_SUCCESS; |
|
155 | + } |
|
156 | + return WordPressOption::UPDATE_ERROR; |
|
157 | + } |
|
158 | + |
|
159 | + |
|
160 | + private function valueIsUnchanged($value): bool |
|
161 | + { |
|
162 | + if (is_array($value) && is_array($this->value)) { |
|
163 | + $diff = EEH_Array::array_diff_recursive($value, $this->value); |
|
164 | + // $diff = array_diff($value, $this->value); |
|
165 | + return empty($diff); |
|
166 | + } |
|
167 | + // emulate WP's method for checking equality |
|
168 | + return $value === $this->value && maybe_serialize($value) === maybe_serialize($this->value); |
|
169 | + } |
|
170 | + |
|
171 | + |
|
172 | + /** |
|
173 | + * @return string |
|
174 | + */ |
|
175 | + private function autoload(): string |
|
176 | + { |
|
177 | + return $this->autoload ? 'yes' : 'no'; |
|
178 | + } |
|
179 | + |
|
180 | + |
|
181 | + /** |
|
182 | + * Deletes the option from the database |
|
183 | + * for the rest of the request |
|
184 | + * |
|
185 | + * @return bool |
|
186 | + * @since 5.0.0.p |
|
187 | + */ |
|
188 | + public function deleteOption(): bool |
|
189 | + { |
|
190 | + return $this->option_engine->deleteOption($this->getOptionName()); |
|
191 | + } |
|
192 | 192 | } |
@@ -137,7 +137,7 @@ |
||
137 | 137 | public function updateOption($value, bool $force_update = false): int |
138 | 138 | { |
139 | 139 | // don't update if value has not changed since last update |
140 | - if (! $force_update && $this->valueIsUnchanged($value)) { |
|
140 | + if ( ! $force_update && $this->valueIsUnchanged($value)) { |
|
141 | 141 | return WordPressOption::UPDATE_NONE; |
142 | 142 | } |
143 | 143 | if ($force_update) { |