WooCommerce PayPal Payments - Version 1.6.1

Version Description

  • Fix - Handle authorization capture failures #312
  • Fix - Handle denied payment authorization #302
  • Fix - Handle failed authorizations when capturing order #303
  • Fix - Transactions cannot be voided #293
  • Fix - Fatal error: get_3ds_contingency() #310
Download this release

Release Info

Developer woothemes
Plugin Icon 128x128 WooCommerce PayPal Payments
Version 1.6.1
Comparing to
See all releases

Code changes from version 1.6.0 to 1.6.1

Files changed (32) hide show
  1. changelog.txt +7 -0
  2. modules/ppcp-api-client/services.php +3 -1
  3. modules/ppcp-api-client/src/Endpoint/class-orderendpoint.php +10 -2
  4. modules/ppcp-api-client/src/Endpoint/class-paymentsendpoint.php +57 -51
  5. modules/ppcp-api-client/src/Entity/class-authorizationstatus.php +21 -3
  6. modules/ppcp-api-client/src/Entity/class-authorizationstatusdetails.php +71 -0
  7. modules/ppcp-api-client/src/Entity/class-capture.php +17 -33
  8. modules/ppcp-api-client/src/Entity/class-capturestatus.php +79 -0
  9. modules/ppcp-api-client/src/Entity/class-capturestatusdetails.php +101 -0
  10. modules/ppcp-api-client/src/Exception/class-paypalapiexception.php +7 -3
  11. modules/ppcp-api-client/src/Factory/class-authorizationfactory.php +7 -1
  12. modules/ppcp-api-client/src/Factory/class-capturefactory.php +8 -3
  13. modules/ppcp-button/src/Assets/class-smartbutton.php +4 -1
  14. modules/ppcp-wc-gateway/services.php +14 -7
  15. modules/ppcp-wc-gateway/src/Admin/class-ordertablepaymentstatuscolumn.php +10 -4
  16. modules/ppcp-wc-gateway/src/Admin/class-paymentstatusorderdetail.php +17 -7
  17. modules/ppcp-wc-gateway/src/Gateway/class-creditcardgateway.php +21 -1
  18. modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php +48 -15
  19. modules/ppcp-wc-gateway/src/Gateway/class-processpaymenttrait.php +57 -33
  20. modules/ppcp-wc-gateway/src/Notice/class-authorizeorderactionnotice.php +19 -11
  21. modules/ppcp-wc-gateway/src/Processor/class-authorizedpaymentsprocessor.php +74 -42
  22. modules/ppcp-wc-gateway/src/Processor/class-ordermetatrait.php +41 -0
  23. modules/ppcp-wc-gateway/src/Processor/class-orderprocessor.php +15 -21
  24. modules/ppcp-wc-gateway/src/Processor/class-paymentstatushandlingtrait.php +141 -0
  25. modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php +113 -23
  26. readme.txt +8 -1
  27. vendor/autoload.php +1 -1
  28. vendor/composer/autoload_classmap.php +5 -0
  29. vendor/composer/autoload_real.php +7 -7
  30. vendor/composer/autoload_static.php +9 -4
  31. vendor/composer/installed.php +2 -2
  32. woocommerce-paypal-payments.php +2 -2
changelog.txt CHANGED
@@ -1,5 +1,12 @@
1
  *** Changelog ***
2
 
 
 
 
 
 
 
 
3
  = 1.6.0 - 2021-09-29 =
4
  * Add - Webhook status. #246 #273
5
  * Add - Show CC gateway in admin payments list. #236
1
  *** Changelog ***
2
 
3
+ = 1.6.1 - 2021-10-12 =
4
+ * Fix - Handle authorization capture failures #312
5
+ * Fix - Handle denied payment authorization #302
6
+ * Fix - Handle failed authorizations when capturing order #303
7
+ * Fix - Transactions cannot be voided #293
8
+ * Fix - Fatal error: get_3ds_contingency() #310
9
+
10
  = 1.6.0 - 2021-09-29 =
11
  * Add - Webhook status. #246 #273
12
  * Add - Show CC gateway in admin payments list. #236
modules/ppcp-api-client/services.php CHANGED
@@ -141,12 +141,14 @@ return array(
141
  },
142
  'api.endpoint.payments' => static function ( $container ): PaymentsEndpoint {
143
  $authorizations_factory = $container->get( 'api.factory.authorization' );
144
- $logger = $container->get( 'woocommerce.logger.woocommerce' );
 
145
 
146
  return new PaymentsEndpoint(
147
  $container->get( 'api.host' ),
148
  $container->get( 'api.bearer' ),
149
  $authorizations_factory,
 
150
  $logger
151
  );
152
  },
141
  },
142
  'api.endpoint.payments' => static function ( $container ): PaymentsEndpoint {
143
  $authorizations_factory = $container->get( 'api.factory.authorization' );
144
+ $capture_factory = $container->get( 'api.factory.capture' );
145
+ $logger = $container->get( 'woocommerce.logger.woocommerce' );
146
 
147
  return new PaymentsEndpoint(
148
  $container->get( 'api.host' ),
149
  $container->get( 'api.bearer' ),
150
  $authorizations_factory,
151
+ $capture_factory,
152
  $logger
153
  );
154
  },
modules/ppcp-api-client/src/Endpoint/class-orderendpoint.php CHANGED
@@ -11,6 +11,8 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
11
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
 
 
14
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
15
  use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
16
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
@@ -338,8 +340,8 @@ class OrderEndpoint {
338
 
339
  $order = $this->order_factory->from_paypal_response( $json );
340
 
341
- $purchase_units_payments_captures_status = $order->purchase_units()[0]->payments()->captures()[0]->status() ?? '';
342
- if ( $purchase_units_payments_captures_status && 'DECLINED' === $purchase_units_payments_captures_status ) {
343
  throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
344
  }
345
 
@@ -412,6 +414,12 @@ class OrderEndpoint {
412
  throw $error;
413
  }
414
  $order = $this->order_factory->from_paypal_response( $json );
 
 
 
 
 
 
415
  return $order;
416
  }
417
 
11
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
14
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
15
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
16
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
17
  use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
18
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
340
 
341
  $order = $this->order_factory->from_paypal_response( $json );
342
 
343
+ $capture_status = $order->purchase_units()[0]->payments()->captures()[0]->status() ?? null;
344
+ if ( $capture_status && $capture_status->is( CaptureStatus::DECLINED ) ) {
345
  throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
346
  }
347
 
414
  throw $error;
415
  }
416
  $order = $this->order_factory->from_paypal_response( $json );
417
+
418
+ $authorization_status = $order->purchase_units()[0]->payments()->authorizations()[0]->status() ?? null;
419
+ if ( $authorization_status && $authorization_status->is( AuthorizationStatus::DENIED ) ) {
420
+ throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
421
+ }
422
+
423
  return $order;
424
  }
425
 
modules/ppcp-api-client/src/Endpoint/class-paymentsendpoint.php CHANGED
@@ -11,11 +11,13 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
11
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
 
14
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
15
  use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
16
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
17
  use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
18
  use Psr\Log\LoggerInterface;
 
19
 
20
  /**
21
  * Class PaymentsEndpoint
@@ -45,6 +47,13 @@ class PaymentsEndpoint {
45
  */
46
  private $authorizations_factory;
47
 
 
 
 
 
 
 
 
48
  /**
49
  * The logger.
50
  *
@@ -58,18 +67,21 @@ class PaymentsEndpoint {
58
  * @param string $host The host.
59
  * @param Bearer $bearer The bearer.
60
  * @param AuthorizationFactory $authorization_factory The authorization factory.
 
61
  * @param LoggerInterface $logger The logger.
62
  */
63
  public function __construct(
64
  string $host,
65
  Bearer $bearer,
66
  AuthorizationFactory $authorization_factory,
 
67
  LoggerInterface $logger
68
  ) {
69
 
70
  $this->host = $host;
71
  $this->bearer = $bearer;
72
  $this->authorizations_factory = $authorization_factory;
 
73
  $this->logger = $logger;
74
  }
75
 
@@ -136,10 +148,11 @@ class PaymentsEndpoint {
136
  *
137
  * @param string $authorization_id The id.
138
  *
139
- * @return Authorization
140
  * @throws RuntimeException If the request fails.
 
141
  */
142
- public function capture( string $authorization_id ): Authorization {
143
  $bearer = $this->bearer->bearer();
144
  $url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture';
145
  $args = array(
@@ -155,39 +168,18 @@ class PaymentsEndpoint {
155
  $json = json_decode( $response['body'] );
156
 
157
  if ( is_wp_error( $response ) ) {
158
- $error = new RuntimeException(
159
- __( 'Could not capture authorized payment.', 'woocommerce-paypal-payments' )
160
- );
161
- $this->logger->log(
162
- 'warning',
163
- $error->getMessage(),
164
- array(
165
- 'args' => $args,
166
- 'response' => $response,
167
- )
168
- );
169
- throw $error;
170
  }
171
 
172
  $status_code = (int) wp_remote_retrieve_response_code( $response );
173
  if ( 201 !== $status_code ) {
174
- $error = new PayPalApiException(
175
  $json,
176
  $status_code
177
  );
178
- $this->logger->log(
179
- 'warning',
180
- $error->getMessage(),
181
- array(
182
- 'args' => $args,
183
- 'response' => $response,
184
- )
185
- );
186
- throw $error;
187
  }
188
 
189
- $authorization = $this->authorizations_factory->from_paypal_response( $json );
190
- return $authorization;
191
  }
192
 
193
  /**
@@ -195,10 +187,11 @@ class PaymentsEndpoint {
195
  *
196
  * @param Refund $refund The refund to be processed.
197
  *
198
- * @return bool
199
  * @throws RuntimeException If the request fails.
 
200
  */
201
- public function refund( Refund $refund ) : bool {
202
  $bearer = $this->bearer->bearer();
203
  $url = trailingslashit( $this->host ) . 'v2/payments/captures/' . $refund->for_capture()->id() . '/refund';
204
  $args = array(
@@ -215,37 +208,50 @@ class PaymentsEndpoint {
215
  $json = json_decode( $response['body'] );
216
 
217
  if ( is_wp_error( $response ) ) {
218
- $error = new RuntimeException(
219
- __( 'Could not refund payment.', 'woocommerce-paypal-payments' )
220
- );
221
- $this->logger->log(
222
- 'warning',
223
- $error->getMessage(),
224
- array(
225
- 'args' => $args,
226
- 'response' => $response,
227
- )
228
- );
229
- throw $error;
230
  }
231
 
232
  $status_code = (int) wp_remote_retrieve_response_code( $response );
233
  if ( 201 !== $status_code ) {
234
- $error = new PayPalApiException(
235
  $json,
236
  $status_code
237
  );
238
- $this->logger->log(
239
- 'warning',
240
- $error->getMessage(),
241
- array(
242
- 'args' => $args,
243
- 'response' => $response,
244
- )
245
- );
246
- throw $error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
 
249
- return true;
 
 
 
 
 
250
  }
251
  }
11
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
14
+ use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
15
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
16
  use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
17
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
18
  use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
19
  use Psr\Log\LoggerInterface;
20
+ use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
21
 
22
  /**
23
  * Class PaymentsEndpoint
47
  */
48
  private $authorizations_factory;
49
 
50
+ /**
51
+ * The capture factory.
52
+ *
53
+ * @var CaptureFactory
54
+ */
55
+ private $capture_factory;
56
+
57
  /**
58
  * The logger.
59
  *
67
  * @param string $host The host.
68
  * @param Bearer $bearer The bearer.
69
  * @param AuthorizationFactory $authorization_factory The authorization factory.
70
+ * @param CaptureFactory $capture_factory The capture factory.
71
  * @param LoggerInterface $logger The logger.
72
  */
73
  public function __construct(
74
  string $host,
75
  Bearer $bearer,
76
  AuthorizationFactory $authorization_factory,
77
+ CaptureFactory $capture_factory,
78
  LoggerInterface $logger
79
  ) {
80
 
81
  $this->host = $host;
82
  $this->bearer = $bearer;
83
  $this->authorizations_factory = $authorization_factory;
84
+ $this->capture_factory = $capture_factory;
85
  $this->logger = $logger;
86
  }
87
 
148
  *
149
  * @param string $authorization_id The id.
150
  *
151
+ * @return Capture
152
  * @throws RuntimeException If the request fails.
153
+ * @throws PayPalApiException If the request fails.
154
  */
155
+ public function capture( string $authorization_id ): Capture {
156
  $bearer = $this->bearer->bearer();
157
  $url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture';
158
  $args = array(
168
  $json = json_decode( $response['body'] );
169
 
170
  if ( is_wp_error( $response ) ) {
171
+ throw new RuntimeException( 'Could not capture authorized payment.' );
 
 
 
 
 
 
 
 
 
 
 
172
  }
173
 
174
  $status_code = (int) wp_remote_retrieve_response_code( $response );
175
  if ( 201 !== $status_code ) {
176
+ throw new PayPalApiException(
177
  $json,
178
  $status_code
179
  );
 
 
 
 
 
 
 
 
 
180
  }
181
 
182
+ return $this->capture_factory->from_paypal_response( $json );
 
183
  }
184
 
185
  /**
187
  *
188
  * @param Refund $refund The refund to be processed.
189
  *
190
+ * @return void
191
  * @throws RuntimeException If the request fails.
192
+ * @throws PayPalApiException If the request fails.
193
  */
194
+ public function refund( Refund $refund ) : void {
195
  $bearer = $this->bearer->bearer();
196
  $url = trailingslashit( $this->host ) . 'v2/payments/captures/' . $refund->for_capture()->id() . '/refund';
197
  $args = array(
208
  $json = json_decode( $response['body'] );
209
 
210
  if ( is_wp_error( $response ) ) {
211
+ throw new RuntimeException( 'Could not refund payment.' );
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
 
214
  $status_code = (int) wp_remote_retrieve_response_code( $response );
215
  if ( 201 !== $status_code ) {
216
+ throw new PayPalApiException(
217
  $json,
218
  $status_code
219
  );
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Voids a transaction.
225
+ *
226
+ * @param Authorization $authorization The PayPal payment authorization to void.
227
+ *
228
+ * @return void
229
+ * @throws RuntimeException If the request fails.
230
+ * @throws PayPalApiException If the request fails.
231
+ */
232
+ public function void( Authorization $authorization ) : void {
233
+ $bearer = $this->bearer->bearer();
234
+ $url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization->id() . '/void';
235
+ $args = array(
236
+ 'method' => 'POST',
237
+ 'headers' => array(
238
+ 'Authorization' => 'Bearer ' . $bearer->token(),
239
+ 'Content-Type' => 'application/json',
240
+ 'Prefer' => 'return=representation',
241
+ ),
242
+ );
243
+
244
+ $response = $this->request( $url, $args );
245
+
246
+ if ( is_wp_error( $response ) ) {
247
+ throw new RuntimeException( 'Could not void transaction.' );
248
  }
249
 
250
+ $status_code = (int) wp_remote_retrieve_response_code( $response );
251
+ // Currently it can return body with 200 status, despite the docs saying that it should be 204 No content.
252
+ // We don't care much about body, so just checking that it was successful.
253
+ if ( $status_code < 200 || $status_code > 299 ) {
254
+ throw new PayPalApiException( null, $status_code );
255
+ }
256
  }
257
  }
modules/ppcp-api-client/src/Entity/class-authorizationstatus.php CHANGED
@@ -44,13 +44,21 @@ class AuthorizationStatus {
44
  */
45
  private $status;
46
 
 
 
 
 
 
 
 
47
  /**
48
  * AuthorizationStatus constructor.
49
  *
50
- * @param string $status The status.
 
51
  * @throws RuntimeException When the status is not valid.
52
  */
53
- public function __construct( string $status ) {
54
  if ( ! in_array( $status, self::VALID_STATUS, true ) ) {
55
  throw new RuntimeException(
56
  sprintf(
@@ -60,7 +68,8 @@ class AuthorizationStatus {
60
  )
61
  );
62
  }
63
- $this->status = $status;
 
64
  }
65
 
66
  /**
@@ -91,4 +100,13 @@ class AuthorizationStatus {
91
  public function name(): string {
92
  return $this->status;
93
  }
 
 
 
 
 
 
 
 
 
94
  }
44
  */
45
  private $status;
46
 
47
+ /**
48
+ * The details.
49
+ *
50
+ * @var AuthorizationStatusDetails|null
51
+ */
52
+ private $details;
53
+
54
  /**
55
  * AuthorizationStatus constructor.
56
  *
57
+ * @param string $status The status.
58
+ * @param AuthorizationStatusDetails|null $details The details.
59
  * @throws RuntimeException When the status is not valid.
60
  */
61
+ public function __construct( string $status, ?AuthorizationStatusDetails $details = null ) {
62
  if ( ! in_array( $status, self::VALID_STATUS, true ) ) {
63
  throw new RuntimeException(
64
  sprintf(
68
  )
69
  );
70
  }
71
+ $this->status = $status;
72
+ $this->details = $details;
73
  }
74
 
75
  /**
100
  public function name(): string {
101
  return $this->status;
102
  }
103
+
104
+ /**
105
+ * Returns the details.
106
+ *
107
+ * @return AuthorizationStatusDetails|null
108
+ */
109
+ public function details(): ?AuthorizationStatusDetails {
110
+ return $this->details;
111
+ }
112
  }
modules/ppcp-api-client/src/Entity/class-authorizationstatusdetails.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The AuthorizationStatusDetails object.
4
+ *
5
+ * @see https://developer.paypal.com/docs/api/payments/v2/#definition-authorization_status_details
6
+ *
7
+ * @package WooCommerce\PayPalCommerce\ApiClient\Entity
8
+ */
9
+
10
+ declare(strict_types=1);
11
+
12
+ namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
13
+
14
+ /**
15
+ * Class AuthorizationStatusDetails
16
+ */
17
+ class AuthorizationStatusDetails {
18
+
19
+ const PENDING_REVIEW = 'PENDING_REVIEW';
20
+
21
+ /**
22
+ * The reason.
23
+ *
24
+ * @var string
25
+ */
26
+ private $reason;
27
+
28
+ /**
29
+ * AuthorizationStatusDetails constructor.
30
+ *
31
+ * @param string $reason The reason explaining authorization status.
32
+ */
33
+ public function __construct( string $reason ) {
34
+ $this->reason = $reason;
35
+ }
36
+
37
+ /**
38
+ * Compares the current reason with a given one.
39
+ *
40
+ * @param string $reason The reason to compare with.
41
+ *
42
+ * @return bool
43
+ */
44
+ public function is( string $reason ): bool {
45
+ return $this->reason === $reason;
46
+ }
47
+
48
+ /**
49
+ * Returns the reason explaining authorization status.
50
+ * One of AuthorizationStatusDetails constants.
51
+ *
52
+ * @return string
53
+ */
54
+ public function reason(): string {
55
+ return $this->reason;
56
+ }
57
+
58
+ /**
59
+ * Returns the human-readable reason text explaining authorization status.
60
+ *
61
+ * @return string
62
+ */
63
+ public function text(): string {
64
+ switch ( $this->reason ) {
65
+ case self::PENDING_REVIEW:
66
+ return __( 'Authorization is pending manual review.', 'woocommerce-paypal-payments' );
67
+ default:
68
+ return $this->reason;
69
+ }
70
+ }
71
+ }
modules/ppcp-api-client/src/Entity/class-capture.php CHANGED
@@ -26,17 +26,10 @@ class Capture {
26
  /**
27
  * The status.
28
  *
29
- * @var string
30
  */
31
  private $status;
32
 
33
- /**
34
- * The status details.
35
- *
36
- * @var string
37
- */
38
- private $status_details;
39
-
40
  /**
41
  * The amount.
42
  *
@@ -75,19 +68,17 @@ class Capture {
75
  /**
76
  * Capture constructor.
77
  *
78
- * @param string $id The ID.
79
- * @param string $status The status.
80
- * @param string $status_details The status details.
81
- * @param Amount $amount The amount.
82
- * @param bool $final_capture The final capture.
83
- * @param string $seller_protection The seller protection.
84
- * @param string $invoice_id The invoice id.
85
- * @param string $custom_id The custom id.
86
  */
87
  public function __construct(
88
  string $id,
89
- string $status,
90
- string $status_details,
91
  Amount $amount,
92
  bool $final_capture,
93
  string $seller_protection,
@@ -97,7 +88,6 @@ class Capture {
97
 
98
  $this->id = $id;
99
  $this->status = $status;
100
- $this->status_details = $status_details;
101
  $this->amount = $amount;
102
  $this->final_capture = $final_capture;
103
  $this->seller_protection = $seller_protection;
@@ -117,21 +107,12 @@ class Capture {
117
  /**
118
  * Returns the status.
119
  *
120
- * @return string
121
  */
122
- public function status() : string {
123
  return $this->status;
124
  }
125
 
126
- /**
127
- * Returns the status details object.
128
- *
129
- * @return \stdClass
130
- */
131
- public function status_details() : \stdClass {
132
- return (object) array( 'reason' => $this->status_details );
133
- }
134
-
135
  /**
136
  * Returns the amount.
137
  *
@@ -183,15 +164,18 @@ class Capture {
183
  * @return array
184
  */
185
  public function to_array() : array {
186
- return array(
187
  'id' => $this->id(),
188
- 'status' => $this->status(),
189
- 'status_details' => (array) $this->status_details(),
190
  'amount' => $this->amount()->to_array(),
191
  'final_capture' => $this->final_capture(),
192
  'seller_protection' => (array) $this->seller_protection(),
193
  'invoice_id' => $this->invoice_id(),
194
  'custom_id' => $this->custom_id(),
195
  );
 
 
 
 
196
  }
197
  }
26
  /**
27
  * The status.
28
  *
29
+ * @var CaptureStatus
30
  */
31
  private $status;
32
 
 
 
 
 
 
 
 
33
  /**
34
  * The amount.
35
  *
68
  /**
69
  * Capture constructor.
70
  *
71
+ * @param string $id The ID.
72
+ * @param CaptureStatus $status The status.
73
+ * @param Amount $amount The amount.
74
+ * @param bool $final_capture The final capture.
75
+ * @param string $seller_protection The seller protection.
76
+ * @param string $invoice_id The invoice id.
77
+ * @param string $custom_id The custom id.
 
78
  */
79
  public function __construct(
80
  string $id,
81
+ CaptureStatus $status,
 
82
  Amount $amount,
83
  bool $final_capture,
84
  string $seller_protection,
88
 
89
  $this->id = $id;
90
  $this->status = $status;
 
91
  $this->amount = $amount;
92
  $this->final_capture = $final_capture;
93
  $this->seller_protection = $seller_protection;
107
  /**
108
  * Returns the status.
109
  *
110
+ * @return CaptureStatus
111
  */
112
+ public function status() : CaptureStatus {
113
  return $this->status;
114
  }
115
 
 
 
 
 
 
 
 
 
 
116
  /**
117
  * Returns the amount.
118
  *
164
  * @return array
165
  */
166
  public function to_array() : array {
167
+ $data = array(
168
  'id' => $this->id(),
169
+ 'status' => $this->status()->name(),
 
170
  'amount' => $this->amount()->to_array(),
171
  'final_capture' => $this->final_capture(),
172
  'seller_protection' => (array) $this->seller_protection(),
173
  'invoice_id' => $this->invoice_id(),
174
  'custom_id' => $this->custom_id(),
175
  );
176
+ if ( $this->status()->details() ) {
177
+ $data['status_details'] = array( 'reason' => $this->status()->details()->reason() );
178
+ }
179
+ return $data;
180
  }
181
  }
modules/ppcp-api-client/src/Entity/class-capturestatus.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The CaptureStatus object.
4
+ *
5
+ * @see https://developer.paypal.com/docs/api/orders/v2/#definition-capture_status
6
+ *
7
+ * @package WooCommerce\PayPalCommerce\ApiClient\Entity
8
+ */
9
+
10
+ declare(strict_types=1);
11
+
12
+ namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
13
+
14
+ /**
15
+ * Class CaptureStatus
16
+ */
17
+ class CaptureStatus {
18
+
19
+ const COMPLETED = 'COMPLETED';
20
+ const DECLINED = 'DECLINED';
21
+ const PARTIALLY_REFUNDED = 'PARTIALLY_REFUNDED';
22
+ const REFUNDED = 'REFUNDED';
23
+ const FAILED = 'FAILED';
24
+ const PENDING = 'PENDING';
25
+
26
+ /**
27
+ * The status.
28
+ *
29
+ * @var string
30
+ */
31
+ private $status;
32
+
33
+ /**
34
+ * The details.
35
+ *
36
+ * @var CaptureStatusDetails|null
37
+ */
38
+ private $details;
39
+
40
+ /**
41
+ * CaptureStatus constructor.
42
+ *
43
+ * @param string $status The status.
44
+ * @param CaptureStatusDetails|null $details The details.
45
+ */
46
+ public function __construct( string $status, ?CaptureStatusDetails $details = null ) {
47
+ $this->status = $status;
48
+ $this->details = $details;
49
+ }
50
+
51
+ /**
52
+ * Compares the current status with a given one.
53
+ *
54
+ * @param string $status The status to compare with.
55
+ *
56
+ * @return bool
57
+ */
58
+ public function is( string $status ): bool {
59
+ return $this->status === $status;
60
+ }
61
+
62
+ /**
63
+ * Returns the status.
64
+ *
65
+ * @return string
66
+ */
67
+ public function name(): string {
68
+ return $this->status;
69
+ }
70
+
71
+ /**
72
+ * Returns the details.
73
+ *
74
+ * @return CaptureStatusDetails|null
75
+ */
76
+ public function details(): ?CaptureStatusDetails {
77
+ return $this->details;
78
+ }
79
+ }
modules/ppcp-api-client/src/Entity/class-capturestatusdetails.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The CaptureStatusDetails object.
4
+ *
5
+ * @see https://developer.paypal.com/docs/api/payments/v2/#definition-capture_status_details
6
+ *
7
+ * @package WooCommerce\PayPalCommerce\ApiClient\Entity
8
+ */
9
+
10
+ declare(strict_types=1);
11
+
12
+ namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
13
+
14
+ /**
15
+ * Class CaptureStatusDetails
16
+ */
17
+ class CaptureStatusDetails {
18
+
19
+ const BUYER_COMPLAINT = 'BUYER_COMPLAINT';
20
+ const CHARGEBACK = 'CHARGEBACK';
21
+ const ECHECK = 'ECHECK';
22
+ const INTERNATIONAL_WITHDRAWAL = 'INTERNATIONAL_WITHDRAWAL';
23
+ const OTHER = 'OTHER';
24
+ const PENDING_REVIEW = 'PENDING_REVIEW';
25
+ const RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION = 'RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION';
26
+ const REFUNDED = 'REFUNDED';
27
+ const TRANSACTION_APPROVED_AWAITING_FUNDING = 'TRANSACTION_APPROVED_AWAITING_FUNDING';
28
+ const UNILATERAL = 'UNILATERAL';
29
+ const VERIFICATION_REQUIRED = 'VERIFICATION_REQUIRED';
30
+
31
+ /**
32
+ * The reason.
33
+ *
34
+ * @var string
35
+ */
36
+ private $reason;
37
+
38
+ /**
39
+ * CaptureStatusDetails constructor.
40
+ *
41
+ * @param string $reason The reason explaining capture status.
42
+ */
43
+ public function __construct( string $reason ) {
44
+ $this->reason = $reason;
45
+ }
46
+
47
+ /**
48
+ * Compares the current reason with a given one.
49
+ *
50
+ * @param string $reason The reason to compare with.
51
+ *
52
+ * @return bool
53
+ */
54
+ public function is( string $reason ): bool {
55
+ return $this->reason === $reason;
56
+ }
57
+
58
+ /**
59
+ * Returns the reason explaining capture status.
60
+ * One of CaptureStatusDetails constants.
61
+ *
62
+ * @return string
63
+ */
64
+ public function reason(): string {
65
+ return $this->reason;
66
+ }
67
+
68
+ /**
69
+ * Returns the human-readable reason text explaining capture status.
70
+ *
71
+ * @return string
72
+ */
73
+ public function text(): string {
74
+ switch ( $this->reason ) {
75
+ case self::BUYER_COMPLAINT:
76
+ return __( 'The payer initiated a dispute for this captured payment with PayPal.', 'woocommerce-paypal-payments' );
77
+ case self::CHARGEBACK:
78
+ return __( 'The captured funds were reversed in response to the payer disputing this captured payment with the issuer of the financial instrument used to pay for this captured payment.', 'woocommerce-paypal-payments' );
79
+ case self::ECHECK:
80
+ return __( 'The payer paid by an eCheck that has not yet cleared.', 'woocommerce-paypal-payments' );
81
+ case self::INTERNATIONAL_WITHDRAWAL:
82
+ return __( 'Visit your online account. In your Account Overview, accept and deny this payment.', 'woocommerce-paypal-payments' );
83
+ case self::OTHER:
84
+ return __( 'No additional specific reason can be provided. For more information about this captured payment, visit your account online or contact PayPal.', 'woocommerce-paypal-payments' );
85
+ case self::PENDING_REVIEW:
86
+ return __( 'The captured payment is pending manual review.', 'woocommerce-paypal-payments' );
87
+ case self::RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION:
88
+ return __( 'The payee has not yet set up appropriate receiving preferences for their account. For more information about how to accept or deny this payment, visit your account online. This reason is typically offered in scenarios such as when the currency of the captured payment is different from the primary holding currency of the payee.', 'woocommerce-paypal-payments' );
89
+ case self::REFUNDED:
90
+ return __( 'The captured funds were refunded.', 'woocommerce-paypal-payments' );
91
+ case self::TRANSACTION_APPROVED_AWAITING_FUNDING:
92
+ return __( 'The payer must send the funds for this captured payment. This code generally appears for manual EFTs.', 'woocommerce-paypal-payments' );
93
+ case self::UNILATERAL:
94
+ return __( 'The payee does not have a PayPal account.', 'woocommerce-paypal-payments' );
95
+ case self::VERIFICATION_REQUIRED:
96
+ return __( 'The payee\'s PayPal account is not verified.', 'woocommerce-paypal-payments' );
97
+ default:
98
+ return $this->reason;
99
+ }
100
+ }
101
+ }
modules/ppcp-api-client/src/Exception/class-paypalapiexception.php CHANGED
@@ -39,9 +39,13 @@ class PayPalApiException extends RuntimeException {
39
  $response = new \stdClass();
40
  }
41
  if ( ! isset( $response->message ) ) {
42
- $response->message = __(
43
- 'Unknown error while connecting to PayPal.',
44
- 'woocommerce-paypal-payments'
 
 
 
 
45
  );
46
  }
47
  if ( ! isset( $response->name ) ) {
39
  $response = new \stdClass();
40
  }
41
  if ( ! isset( $response->message ) ) {
42
+ $response->message = sprintf(
43
+ /* translators: %1$d - HTTP status code number (404, 500, ...) */
44
+ __(
45
+ 'Unknown error while connecting to PayPal. Status code: %1$d.',
46
+ 'woocommerce-paypal-payments'
47
+ ),
48
+ $this->status_code
49
  );
50
  }
51
  if ( ! isset( $response->name ) ) {
modules/ppcp-api-client/src/Factory/class-authorizationfactory.php CHANGED
@@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
11
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
 
14
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
15
 
16
  /**
@@ -39,9 +40,14 @@ class AuthorizationFactory {
39
  );
40
  }
41
 
 
 
42
  return new Authorization(
43
  $data->id,
44
- new AuthorizationStatus( $data->status )
 
 
 
45
  );
46
  }
47
  }
11
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
14
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatusDetails;
15
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
16
 
17
  /**
40
  );
41
  }
42
 
43
+ $reason = $data->status_details->reason ?? null;
44
+
45
  return new Authorization(
46
  $data->id,
47
+ new AuthorizationStatus(
48
+ $data->status,
49
+ $reason ? new AuthorizationStatusDetails( $reason ) : null
50
+ )
51
  );
52
  }
53
  }
modules/ppcp-api-client/src/Factory/class-capturefactory.php CHANGED
@@ -10,6 +10,8 @@ declare( strict_types=1 );
10
  namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
11
 
12
  use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
 
 
13
 
14
  /**
15
  * Class CaptureFactory
@@ -42,11 +44,14 @@ class CaptureFactory {
42
  */
43
  public function from_paypal_response( \stdClass $data ) : Capture {
44
 
45
- $reason = isset( $data->status_details->reason ) ? (string) $data->status_details->reason : '';
 
46
  return new Capture(
47
  (string) $data->id,
48
- (string) $data->status,
49
- $reason,
 
 
50
  $this->amount_factory->from_paypal_response( $data->amount ),
51
  (bool) $data->final_capture,
52
  (string) $data->seller_protection->status,
10
  namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
11
 
12
  use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
13
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
14
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatusDetails;
15
 
16
  /**
17
  * Class CaptureFactory
44
  */
45
  public function from_paypal_response( \stdClass $data ) : Capture {
46
 
47
+ $reason = $data->status_details->reason ?? null;
48
+
49
  return new Capture(
50
  (string) $data->id,
51
+ new CaptureStatus(
52
+ (string) $data->status,
53
+ $reason ? new CaptureStatusDetails( $reason ) : null
54
+ ),
55
  $this->amount_factory->from_paypal_response( $data->amount ),
56
  (bool) $data->final_capture,
57
  (string) $data->seller_protection->status,
modules/ppcp-button/src/Assets/class-smartbutton.php CHANGED
@@ -608,7 +608,10 @@ class SmartButton implements SmartButtonInterface {
608
  */
609
  private function get_3ds_contingency(): string {
610
  if ( $this->settings->has( '3d_secure_contingency' ) ) {
611
- return $this->settings->get( '3d_secure_contingency' );
 
 
 
612
  }
613
 
614
  return 'SCA_WHEN_REQUIRED';
608
  */
609
  private function get_3ds_contingency(): string {
610
  if ( $this->settings->has( '3d_secure_contingency' ) ) {
611
+ $value = $this->settings->get( '3d_secure_contingency' );
612
+ if ( $value ) {
613
+ return $value;
614
+ }
615
  }
616
 
617
  return 'SCA_WHEN_REQUIRED';
modules/ppcp-wc-gateway/services.php CHANGED
@@ -53,6 +53,7 @@ return array(
53
  $transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' );
54
  $subscription_helper = $container->get( 'subscription.helper' );
55
  $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
 
56
  return new PayPalGateway(
57
  $settings_renderer,
58
  $order_processor,
@@ -64,7 +65,8 @@ return array(
64
  $state,
65
  $transaction_url_provider,
66
  $subscription_helper,
67
- $page_id
 
68
  );
69
  },
70
  'wcgateway.credit-card-gateway' => static function ( $container ): CreditCardGateway {
@@ -83,7 +85,8 @@ return array(
83
  $payer_factory = $container->get( 'api.factory.payer' );
84
  $order_endpoint = $container->get( 'api.endpoint.order' );
85
  $subscription_helper = $container->get( 'subscription.helper' );
86
- $logger = $container->get( 'woocommerce.logger.woocommerce' );
 
87
  return new CreditCardGateway(
88
  $settings_renderer,
89
  $order_processor,
@@ -100,7 +103,8 @@ return array(
100
  $payer_factory,
101
  $order_endpoint,
102
  $subscription_helper,
103
- $logger
 
104
  );
105
  },
106
  'wcgateway.disabler' => static function ( $container ): DisableGateways {
@@ -209,25 +213,28 @@ return array(
209
  $authorized_payments_processor,
210
  $settings,
211
  $logger,
212
- $environment->current_environment_is( Environment::SANDBOX )
213
  );
214
  },
215
  'wcgateway.processor.refunds' => static function ( $container ): RefundProcessor {
216
  $order_endpoint = $container->get( 'api.endpoint.order' );
217
  $payments_endpoint = $container->get( 'api.endpoint.payments' );
218
- return new RefundProcessor( $order_endpoint, $payments_endpoint );
 
219
  },
220
  'wcgateway.processor.authorized-payments' => static function ( $container ): AuthorizedPaymentsProcessor {
221
  $order_endpoint = $container->get( 'api.endpoint.order' );
222
  $payments_endpoint = $container->get( 'api.endpoint.payments' );
223
- return new AuthorizedPaymentsProcessor( $order_endpoint, $payments_endpoint );
 
224
  },
225
  'wcgateway.admin.render-authorize-action' => static function ( $container ): RenderAuthorizeAction {
226
 
227
  return new RenderAuthorizeAction();
228
  },
229
  'wcgateway.admin.order-payment-status' => static function ( $container ): PaymentStatusOrderDetail {
230
- return new PaymentStatusOrderDetail();
 
231
  },
232
  'wcgateway.admin.orders-payment-status-column' => static function ( $container ): OrderTablePaymentStatusColumn {
233
  $settings = $container->get( 'wcgateway.settings' );
53
  $transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' );
54
  $subscription_helper = $container->get( 'subscription.helper' );
55
  $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
56
+ $environment = $container->get( 'onboarding.environment' );
57
  return new PayPalGateway(
58
  $settings_renderer,
59
  $order_processor,
65
  $state,
66
  $transaction_url_provider,
67
  $subscription_helper,
68
+ $page_id,
69
+ $environment
70
  );
71
  },
72
  'wcgateway.credit-card-gateway' => static function ( $container ): CreditCardGateway {
85
  $payer_factory = $container->get( 'api.factory.payer' );
86
  $order_endpoint = $container->get( 'api.endpoint.order' );
87
  $subscription_helper = $container->get( 'subscription.helper' );
88
+ $logger = $container->get( 'woocommerce.logger.woocommerce' );
89
+ $environment = $container->get( 'onboarding.environment' );
90
  return new CreditCardGateway(
91
  $settings_renderer,
92
  $order_processor,
103
  $payer_factory,
104
  $order_endpoint,
105
  $subscription_helper,
106
+ $logger,
107
+ $environment
108
  );
109
  },
110
  'wcgateway.disabler' => static function ( $container ): DisableGateways {
213
  $authorized_payments_processor,
214
  $settings,
215
  $logger,
216
+ $environment
217
  );
218
  },
219
  'wcgateway.processor.refunds' => static function ( $container ): RefundProcessor {
220
  $order_endpoint = $container->get( 'api.endpoint.order' );
221
  $payments_endpoint = $container->get( 'api.endpoint.payments' );
222
+ $logger = $container->get( 'woocommerce.logger.woocommerce' );
223
+ return new RefundProcessor( $order_endpoint, $payments_endpoint, $logger );
224
  },
225
  'wcgateway.processor.authorized-payments' => static function ( $container ): AuthorizedPaymentsProcessor {
226
  $order_endpoint = $container->get( 'api.endpoint.order' );
227
  $payments_endpoint = $container->get( 'api.endpoint.payments' );
228
+ $logger = $container->get( 'woocommerce.logger.woocommerce' );
229
+ return new AuthorizedPaymentsProcessor( $order_endpoint, $payments_endpoint, $logger );
230
  },
231
  'wcgateway.admin.render-authorize-action' => static function ( $container ): RenderAuthorizeAction {
232
 
233
  return new RenderAuthorizeAction();
234
  },
235
  'wcgateway.admin.order-payment-status' => static function ( $container ): PaymentStatusOrderDetail {
236
+ $column = $container->get( 'wcgateway.admin.orders-payment-status-column' );
237
+ return new PaymentStatusOrderDetail( $column );
238
  },
239
  'wcgateway.admin.orders-payment-status-column' => static function ( $container ): OrderTablePaymentStatusColumn {
240
  $settings = $container->get( 'wcgateway.settings' );
modules/ppcp-wc-gateway/src/Admin/class-ordertablepaymentstatuscolumn.php CHANGED
@@ -81,7 +81,7 @@ class OrderTablePaymentStatusColumn {
81
 
82
  $wc_order = wc_get_order( $wc_order_id );
83
 
84
- if ( ! is_a( $wc_order, \WC_Order::class ) || ! $this->render_for_order( $wc_order ) ) {
85
  return;
86
  }
87
 
@@ -100,8 +100,14 @@ class OrderTablePaymentStatusColumn {
100
  *
101
  * @return bool
102
  */
103
- private function render_for_order( \WC_Order $order ): bool {
104
- return ! empty( $order->get_meta( PayPalGateway::CAPTURED_META_KEY ) );
 
 
 
 
 
 
105
  }
106
 
107
  /**
@@ -111,7 +117,7 @@ class OrderTablePaymentStatusColumn {
111
  *
112
  * @return bool
113
  */
114
- private function is_captured( \WC_Order $wc_order ): bool {
115
  $captured = $wc_order->get_meta( PayPalGateway::CAPTURED_META_KEY );
116
  return wc_string_to_bool( $captured );
117
  }
81
 
82
  $wc_order = wc_get_order( $wc_order_id );
83
 
84
+ if ( ! is_a( $wc_order, \WC_Order::class ) || ! $this->should_render_for_order( $wc_order ) ) {
85
  return;
86
  }
87
 
100
  *
101
  * @return bool
102
  */
103
+ public function should_render_for_order( \WC_Order $order ): bool {
104
+ $intent = $order->get_meta( PayPalGateway::INTENT_META_KEY );
105
+ $captured = $order->get_meta( PayPalGateway::CAPTURED_META_KEY );
106
+ $status = $order->get_status();
107
+ $not_allowed_statuses = array( 'refunded' );
108
+ return ! empty( $intent ) && strtoupper( self::INTENT ) === strtoupper( $intent ) &&
109
+ ! empty( $captured ) &&
110
+ ! in_array( $status, $not_allowed_statuses, true );
111
  }
112
 
113
  /**
117
  *
118
  * @return bool
119
  */
120
+ public function is_captured( \WC_Order $wc_order ): bool {
121
  $captured = $wc_order->get_meta( PayPalGateway::CAPTURED_META_KEY );
122
  return wc_string_to_bool( $captured );
123
  }
modules/ppcp-wc-gateway/src/Admin/class-paymentstatusorderdetail.php CHANGED
@@ -16,6 +16,22 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
16
  */
17
  class PaymentStatusOrderDetail {
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  /**
20
  * Renders the not captured information.
21
  *
@@ -23,14 +39,8 @@ class PaymentStatusOrderDetail {
23
  */
24
  public function render( int $wc_order_id ) {
25
  $wc_order = new \WC_Order( $wc_order_id );
26
- $intent = $wc_order->get_meta( PayPalGateway::INTENT_META_KEY );
27
- $captured = $wc_order->get_meta( PayPalGateway::CAPTURED_META_KEY );
28
-
29
- if ( strcasecmp( $intent, 'AUTHORIZE' ) !== 0 ) {
30
- return;
31
- }
32
 
33
- if ( ! empty( $captured ) && wc_string_to_bool( $captured ) ) {
34
  return;
35
  }
36
 
16
  */
17
  class PaymentStatusOrderDetail {
18
 
19
+ /**
20
+ * The capture info column.
21
+ *
22
+ * @var OrderTablePaymentStatusColumn
23
+ */
24
+ private $column;
25
+
26
+ /**
27
+ * PaymentStatusOrderDetail constructor.
28
+ *
29
+ * @param OrderTablePaymentStatusColumn $column The capture info column.
30
+ */
31
+ public function __construct( OrderTablePaymentStatusColumn $column ) {
32
+ $this->column = $column;
33
+ }
34
+
35
  /**
36
  * Renders the not captured information.
37
  *
39
  */
40
  public function render( int $wc_order_id ) {
41
  $wc_order = new \WC_Order( $wc_order_id );
 
 
 
 
 
 
42
 
43
+ if ( ! $this->column->should_render_for_order( $wc_order ) || $this->column->is_captured( $wc_order ) ) {
44
  return;
45
  }
46
 
modules/ppcp-wc-gateway/src/Gateway/class-creditcardgateway.php CHANGED
@@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface;
13
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
14
  use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
15
  use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
 
16
  use WooCommerce\PayPalCommerce\Onboarding\State;
17
  use WooCommerce\PayPalCommerce\Session\SessionHandler;
18
  use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
@@ -96,6 +97,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
96
  */
97
  private $order_endpoint;
98
 
 
 
 
 
 
 
 
99
  /**
100
  * CreditCardGateway constructor.
101
  *
@@ -115,6 +123,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
115
  * @param OrderEndpoint $order_endpoint The order endpoint.
116
  * @param SubscriptionHelper $subscription_helper The subscription helper.
117
  * @param LoggerInterface $logger The logger.
 
118
  */
119
  public function __construct(
120
  SettingsRenderer $settings_renderer,
@@ -132,7 +141,8 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
132
  PayerFactory $payer_factory,
133
  OrderEndpoint $order_endpoint,
134
  SubscriptionHelper $subscription_helper,
135
- LoggerInterface $logger
 
136
  ) {
137
 
138
  $this->id = self::ID;
@@ -143,6 +153,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
143
  $this->config = $config;
144
  $this->session_handler = $session_handler;
145
  $this->refund_processor = $refund_processor;
 
146
 
147
  if ( $state->current_state() === State::STATE_ONBOARDED ) {
148
  $this->supports = array( 'refunds' );
@@ -424,4 +435,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
424
  private function is_enabled(): bool {
425
  return $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' );
426
  }
 
 
 
 
 
 
 
 
 
427
  }
13
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
14
  use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
15
  use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
16
+ use WooCommerce\PayPalCommerce\Onboarding\Environment;
17
  use WooCommerce\PayPalCommerce\Onboarding\State;
18
  use WooCommerce\PayPalCommerce\Session\SessionHandler;
19
  use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
97
  */
98
  private $order_endpoint;
99
 
100
+ /**
101
+ * The environment.
102
+ *
103
+ * @var Environment
104
+ */
105
+ protected $environment;
106
+
107
  /**
108
  * CreditCardGateway constructor.
109
  *
123
  * @param OrderEndpoint $order_endpoint The order endpoint.
124
  * @param SubscriptionHelper $subscription_helper The subscription helper.
125
  * @param LoggerInterface $logger The logger.
126
+ * @param Environment $environment The environment.
127
  */
128
  public function __construct(
129
  SettingsRenderer $settings_renderer,
141
  PayerFactory $payer_factory,
142
  OrderEndpoint $order_endpoint,
143
  SubscriptionHelper $subscription_helper,
144
+ LoggerInterface $logger,
145
+ Environment $environment
146
  ) {
147
 
148
  $this->id = self::ID;
153
  $this->config = $config;
154
  $this->session_handler = $session_handler;
155
  $this->refund_processor = $refund_processor;
156
+ $this->environment = $environment;
157
 
158
  if ( $state->current_state() === State::STATE_ONBOARDED ) {
159
  $this->supports = array( 'refunds' );
435
  private function is_enabled(): bool {
436
  return $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' );
437
  }
438
+
439
+ /**
440
+ * Returns the environment.
441
+ *
442
+ * @return Environment
443
+ */
444
+ protected function environment(): Environment {
445
+ return $this->environment;
446
+ }
447
  }
modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php CHANGED
@@ -9,6 +9,8 @@ declare(strict_types=1);
9
 
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
11
 
 
 
12
  use WooCommerce\PayPalCommerce\Onboarding\State;
13
  use WooCommerce\PayPalCommerce\Session\SessionHandler;
14
  use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
@@ -111,6 +113,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
111
  */
112
  protected $page_id;
113
 
 
 
 
 
 
 
 
114
  /**
115
  * PayPalGateway constructor.
116
  *
@@ -125,6 +134,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
125
  * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
126
  * @param SubscriptionHelper $subscription_helper The subscription helper.
127
  * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
 
128
  */
129
  public function __construct(
130
  SettingsRenderer $settings_renderer,
@@ -137,7 +147,8 @@ class PayPalGateway extends \WC_Payment_Gateway {
137
  State $state,
138
  TransactionUrlProvider $transaction_url_provider,
139
  SubscriptionHelper $subscription_helper,
140
- string $page_id
 
141
  ) {
142
 
143
  $this->id = self::ID;
@@ -150,6 +161,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
150
  $this->refund_processor = $refund_processor;
151
  $this->transaction_url_provider = $transaction_url_provider;
152
  $this->page_id = $page_id;
 
153
  $this->onboarded = $state->current_state() === State::STATE_ONBOARDED;
154
 
155
  if ( $this->onboarded ) {
@@ -239,31 +251,42 @@ class PayPalGateway extends \WC_Payment_Gateway {
239
  * @return bool
240
  */
241
  public function capture_authorized_payment( \WC_Order $wc_order ): bool {
242
- $is_processed = $this->authorized_payments->process( $wc_order );
243
- $this->render_authorization_message_for_status( $this->authorized_payments->last_status() );
 
 
 
 
 
 
 
244
 
245
- if ( $is_processed ) {
246
- $wc_order->add_order_note(
247
- __( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
248
- );
249
  $wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
250
  $wc_order->save();
251
  $wc_order->payment_complete();
252
  return true;
253
  }
254
 
255
- if ( $this->authorized_payments->last_status() === AuthorizedPaymentsProcessor::ALREADY_CAPTURED ) {
256
- if ( $wc_order->get_status() === 'on-hold' ) {
 
 
 
 
 
 
 
 
 
257
  $wc_order->add_order_note(
258
  __( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
259
  );
260
  }
261
-
262
  $wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
263
  $wc_order->save();
264
- $wc_order->payment_complete();
265
  return true;
266
  }
 
267
  return false;
268
  }
269
 
@@ -275,10 +298,11 @@ class PayPalGateway extends \WC_Payment_Gateway {
275
  private function render_authorization_message_for_status( string $status ) {
276
 
277
  $message_mapping = array(
278
- AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
279
- AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
280
- AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
281
- AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
 
282
  );
283
  $display_message = ( isset( $message_mapping[ $status ] ) ) ?
284
  $message_mapping[ $status ]
@@ -429,4 +453,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
429
 
430
  return $ret;
431
  }
 
 
 
 
 
 
 
 
 
432
  }
9
 
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
11
 
12
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
13
+ use WooCommerce\PayPalCommerce\Onboarding\Environment;
14
  use WooCommerce\PayPalCommerce\Onboarding\State;
15
  use WooCommerce\PayPalCommerce\Session\SessionHandler;
16
  use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
113
  */
114
  protected $page_id;
115
 
116
+ /**
117
+ * The environment.
118
+ *
119
+ * @var Environment
120
+ */
121
+ protected $environment;
122
+
123
  /**
124
  * PayPalGateway constructor.
125
  *
134
  * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
135
  * @param SubscriptionHelper $subscription_helper The subscription helper.
136
  * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
137
+ * @param Environment $environment The environment.
138
  */
139
  public function __construct(
140
  SettingsRenderer $settings_renderer,
147
  State $state,
148
  TransactionUrlProvider $transaction_url_provider,
149
  SubscriptionHelper $subscription_helper,
150
+ string $page_id,
151
+ Environment $environment
152
  ) {
153
 
154
  $this->id = self::ID;
161
  $this->refund_processor = $refund_processor;
162
  $this->transaction_url_provider = $transaction_url_provider;
163
  $this->page_id = $page_id;
164
+ $this->environment = $environment;
165
  $this->onboarded = $state->current_state() === State::STATE_ONBOARDED;
166
 
167
  if ( $this->onboarded ) {
251
  * @return bool
252
  */
253
  public function capture_authorized_payment( \WC_Order $wc_order ): bool {
254
+ $result_status = $this->authorized_payments->process( $wc_order );
255
+ $this->render_authorization_message_for_status( $result_status );
256
+
257
+ if ( AuthorizedPaymentsProcessor::ALREADY_CAPTURED === $result_status ) {
258
+ if ( $wc_order->get_status() === 'on-hold' ) {
259
+ $wc_order->add_order_note(
260
+ __( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
261
+ );
262
+ }
263
 
 
 
 
 
264
  $wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
265
  $wc_order->save();
266
  $wc_order->payment_complete();
267
  return true;
268
  }
269
 
270
+ $captures = $this->authorized_payments->captures();
271
+ if ( empty( $captures ) ) {
272
+ return false;
273
+ }
274
+
275
+ $capture = end( $captures );
276
+
277
+ $this->handle_capture_status( $capture, $wc_order );
278
+
279
+ if ( AuthorizedPaymentsProcessor::SUCCESSFUL === $result_status ) {
280
+ if ( $capture->status()->is( CaptureStatus::COMPLETED ) ) {
281
  $wc_order->add_order_note(
282
  __( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
283
  );
284
  }
 
285
  $wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
286
  $wc_order->save();
 
287
  return true;
288
  }
289
+
290
  return false;
291
  }
292
 
298
  private function render_authorization_message_for_status( string $status ) {
299
 
300
  $message_mapping = array(
301
+ AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
302
+ AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
303
+ AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
304
+ AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
305
+ AuthorizedPaymentsProcessor::BAD_AUTHORIZATION => AuthorizeOrderActionNotice::BAD_AUTHORIZATION,
306
  );
307
  $display_message = ( isset( $message_mapping[ $status ] ) ) ?
308
  $message_mapping[ $status ]
453
 
454
  return $ret;
455
  }
456
+
457
+ /**
458
+ * Returns the environment.
459
+ *
460
+ * @return Environment
461
+ */
462
+ protected function environment(): Environment {
463
+ return $this->environment;
464
+ }
465
  }
modules/ppcp-wc-gateway/src/Gateway/class-processpaymenttrait.php CHANGED
@@ -9,14 +9,21 @@ declare( strict_types=1 );
9
 
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
11
 
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
13
  use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
14
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
 
 
 
15
 
16
  /**
17
  * Trait ProcessPaymentTrait
18
  */
19
  trait ProcessPaymentTrait {
 
 
 
20
  /**
21
  * Process a payment for an WooCommerce order.
22
  *
@@ -73,41 +80,37 @@ trait ProcessPaymentTrait {
73
  $selected_token
74
  );
75
 
76
- if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'CAPTURE' ) {
77
- $wc_order->update_status(
78
- 'processing',
79
- __( 'Payment received.', 'woocommerce-paypal-payments' )
80
- );
81
 
82
- $this->session_handler->destroy_session_data();
83
- return array(
84
- 'result' => 'success',
85
- 'redirect' => $this->get_return_url( $wc_order ),
86
- );
87
  }
88
 
89
- if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'AUTHORIZE' ) {
90
- $this->order_endpoint->authorize( $order );
91
- $wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
92
- $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
93
- $wc_order->update_status(
94
- 'on-hold',
95
- __( 'Awaiting payment.', 'woocommerce-paypal-payments' )
96
- );
97
 
98
- $this->session_handler->destroy_session_data();
99
- return array(
100
- 'result' => 'success',
101
- 'redirect' => $this->get_return_url( $wc_order ),
102
- );
103
  }
104
 
105
- $this->logger->warning( "Could neither capture nor authorize order {$order->id()} using a saved credit card:" . 'Status: ' . $order->status()->name() . ' Intent: ' . $order->intent() );
106
 
107
- } catch ( RuntimeException $error ) {
108
- $this->logger->error( $error->getMessage() );
109
  $this->session_handler->destroy_session_data();
110
- wc_add_notice( $error->getMessage(), 'error' );
 
 
 
 
 
111
  return null;
112
  }
113
  }
@@ -186,12 +189,7 @@ trait ProcessPaymentTrait {
186
 
187
  $this->session_handler->destroy_session_data();
188
  } catch ( RuntimeException $error ) {
189
- $wc_order->update_status(
190
- 'failed',
191
- __( 'Could not process order.', 'woocommerce-paypal-payments' )
192
- );
193
- $this->session_handler->destroy_session_data();
194
- wc_add_notice( $error->getMessage(), 'error' );
195
  return $failure_data;
196
  }
197
 
@@ -234,4 +232,30 @@ trait ProcessPaymentTrait {
234
  }
235
  return false;
236
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  }
9
 
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
11
 
12
+ use Exception;
13
  use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
14
  use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
15
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
16
+ use WooCommerce\PayPalCommerce\Onboarding\Environment;
17
+ use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
18
+ use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait;
19
 
20
  /**
21
  * Trait ProcessPaymentTrait
22
  */
23
  trait ProcessPaymentTrait {
24
+
25
+ use OrderMetaTrait, PaymentsStatusHandlingTrait;
26
+
27
  /**
28
  * Process a payment for an WooCommerce order.
29
  *
80
  $selected_token
81
  );
82
 
83
+ $this->add_paypal_meta( $wc_order, $order, $this->environment() );
 
 
 
 
84
 
85
+ if ( ! $order->status()->is( OrderStatus::COMPLETED ) ) {
86
+ $this->logger->warning( "Unexpected status for order {$order->id()} using a saved credit card: " . $order->status()->name() );
87
+ return null;
 
 
88
  }
89
 
90
+ if ( ! in_array(
91
+ $order->intent(),
92
+ array( 'CAPTURE', 'AUTHORIZE' ),
93
+ true
94
+ ) ) {
95
+ $this->logger->warning( "Could neither capture nor authorize order {$order->id()} using a saved credit card:" . 'Status: ' . $order->status()->name() . ' Intent: ' . $order->intent() );
96
+ return null;
97
+ }
98
 
99
+ if ( $order->intent() === 'AUTHORIZE' ) {
100
+ $order = $this->order_endpoint->authorize( $order );
101
+
102
+ $wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
 
103
  }
104
 
105
+ $this->handle_new_order_status( $order, $wc_order );
106
 
 
 
107
  $this->session_handler->destroy_session_data();
108
+ return array(
109
+ 'result' => 'success',
110
+ 'redirect' => $this->get_return_url( $wc_order ),
111
+ );
112
+ } catch ( RuntimeException $error ) {
113
+ $this->handle_failure( $wc_order, $error );
114
  return null;
115
  }
116
  }
189
 
190
  $this->session_handler->destroy_session_data();
191
  } catch ( RuntimeException $error ) {
192
+ $this->handle_failure( $wc_order, $error );
 
 
 
 
 
193
  return $failure_data;
194
  }
195
 
232
  }
233
  return false;
234
  }
235
+
236
+ /**
237
+ * Handles the payment failure.
238
+ *
239
+ * @param \WC_Order $wc_order The order.
240
+ * @param Exception $error The error causing the failure.
241
+ */
242
+ protected function handle_failure( \WC_Order $wc_order, Exception $error ): void {
243
+ $this->logger->error( 'Payment failed: ' . $error->getMessage() );
244
+
245
+ $wc_order->update_status(
246
+ 'failed',
247
+ __( 'Could not process order.', 'woocommerce-paypal-payments' )
248
+ );
249
+
250
+ $this->session_handler->destroy_session_data();
251
+
252
+ wc_add_notice( $error->getMessage(), 'error' );
253
+ }
254
+
255
+ /**
256
+ * Returns the environment.
257
+ *
258
+ * @return Environment
259
+ */
260
+ abstract protected function environment(): Environment;
261
  }
modules/ppcp-wc-gateway/src/Notice/class-authorizeorderactionnotice.php CHANGED
@@ -18,11 +18,12 @@ class AuthorizeOrderActionNotice {
18
 
19
  const QUERY_PARAM = 'ppcp-authorized-message';
20
 
21
- const NO_INFO = 81;
22
- const ALREADY_CAPTURED = 82;
23
- const FAILED = 83;
24
- const SUCCESS = 84;
25
- const NOT_FOUND = 85;
 
26
 
27
  /**
28
  * Returns the current message if there is one.
@@ -45,35 +46,42 @@ class AuthorizeOrderActionNotice {
45
  * @return array
46
  */
47
  private function current_message(): array {
48
- $messages[ self::NO_INFO ] = array(
49
  'message' => __(
50
  'Could not retrieve information. Try again later.',
51
  'woocommerce-paypal-payments'
52
  ),
53
  'type' => 'error',
54
  );
55
- $messages[ self::ALREADY_CAPTURED ] = array(
56
  'message' => __(
57
  'Payment already captured.',
58
  'woocommerce-paypal-payments'
59
  ),
60
  'type' => 'error',
61
  );
62
- $messages[ self::FAILED ] = array(
63
  'message' => __(
64
- 'Failed to capture. Try again later.',
65
  'woocommerce-paypal-payments'
66
  ),
67
  'type' => 'error',
68
  );
69
- $messages[ self::NOT_FOUND ] = array(
 
 
 
 
 
 
 
70
  'message' => __(
71
  'Could not find payment to process.',
72
  'woocommerce-paypal-payments'
73
  ),
74
  'type' => 'error',
75
  );
76
- $messages[ self::SUCCESS ] = array(
77
  'message' => __(
78
  'Payment successfully captured.',
79
  'woocommerce-paypal-payments'
18
 
19
  const QUERY_PARAM = 'ppcp-authorized-message';
20
 
21
+ const NO_INFO = 81;
22
+ const ALREADY_CAPTURED = 82;
23
+ const FAILED = 83;
24
+ const SUCCESS = 84;
25
+ const NOT_FOUND = 85;
26
+ const BAD_AUTHORIZATION = 86;
27
 
28
  /**
29
  * Returns the current message if there is one.
46
  * @return array
47
  */
48
  private function current_message(): array {
49
+ $messages[ self::NO_INFO ] = array(
50
  'message' => __(
51
  'Could not retrieve information. Try again later.',
52
  'woocommerce-paypal-payments'
53
  ),
54
  'type' => 'error',
55
  );
56
+ $messages[ self::ALREADY_CAPTURED ] = array(
57
  'message' => __(
58
  'Payment already captured.',
59
  'woocommerce-paypal-payments'
60
  ),
61
  'type' => 'error',
62
  );
63
+ $messages[ self::FAILED ] = array(
64
  'message' => __(
65
+ 'Failed to capture. Try again later or checks the logs.',
66
  'woocommerce-paypal-payments'
67
  ),
68
  'type' => 'error',
69
  );
70
+ $messages[ self::BAD_AUTHORIZATION ] = array(
71
+ 'message' => __(
72
+ 'Cannot capture, no valid payment authorization.',
73
+ 'woocommerce-paypal-payments'
74
+ ),
75
+ 'type' => 'error',
76
+ );
77
+ $messages[ self::NOT_FOUND ] = array(
78
  'message' => __(
79
  'Could not find payment to process.',
80
  'woocommerce-paypal-payments'
81
  ),
82
  'type' => 'error',
83
  );
84
+ $messages[ self::SUCCESS ] = array(
85
  'message' => __(
86
  'Payment successfully captured.',
87
  'woocommerce-paypal-payments'
modules/ppcp-wc-gateway/src/Processor/class-authorizedpaymentsprocessor.php CHANGED
@@ -10,10 +10,12 @@ declare(strict_types=1);
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
11
 
12
  use Exception;
 
13
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
14
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
15
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
16
  use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
 
17
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
18
  use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
19
 
@@ -22,11 +24,14 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
22
  */
23
  class AuthorizedPaymentsProcessor {
24
 
25
- const SUCCESSFUL = 'SUCCESSFUL';
26
- const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
27
- const FAILED = 'FAILED';
28
- const INACCESSIBLE = 'INACCESSIBLE';
29
- const NOT_FOUND = 'NOT_FOUND';
 
 
 
30
 
31
  /**
32
  * The Order endpoint.
@@ -43,25 +48,35 @@ class AuthorizedPaymentsProcessor {
43
  private $payments_endpoint;
44
 
45
  /**
46
- * The last status.
 
 
 
 
 
 
 
47
  *
48
- * @var string
49
  */
50
- private $last_status = '';
51
 
52
  /**
53
  * AuthorizedPaymentsProcessor constructor.
54
  *
55
  * @param OrderEndpoint $order_endpoint The Order endpoint.
56
  * @param PaymentsEndpoint $payments_endpoint The Payments endpoint.
 
57
  */
58
  public function __construct(
59
  OrderEndpoint $order_endpoint,
60
- PaymentsEndpoint $payments_endpoint
 
61
  ) {
62
 
63
  $this->order_endpoint = $order_endpoint;
64
  $this->payments_endpoint = $payments_endpoint;
 
65
  }
66
 
67
  /**
@@ -69,46 +84,47 @@ class AuthorizedPaymentsProcessor {
69
  *
70
  * @param \WC_Order $wc_order The WooCommerce order.
71
  *
72
- * @return bool
73
  */
74
- public function process( \WC_Order $wc_order ): bool {
 
 
75
  try {
76
  $order = $this->paypal_order_from_wc_order( $wc_order );
77
  } catch ( Exception $exception ) {
78
  if ( $exception->getCode() === 404 ) {
79
- $this->last_status = self::NOT_FOUND;
80
- return false;
81
  }
82
- $this->last_status = self::INACCESSIBLE;
83
- return false;
84
  }
85
 
86
  $authorizations = $this->all_authorizations( $order );
87
 
88
- if ( ! $this->are_authorzations_to_capture( ...$authorizations ) ) {
89
- $this->last_status = self::ALREADY_CAPTURED;
90
- return false;
 
 
 
91
  }
92
 
93
  try {
94
  $this->capture_authorizations( ...$authorizations );
95
  } catch ( Exception $exception ) {
96
- $this->last_status = self::FAILED;
97
- return false;
98
  }
99
 
100
- $this->last_status = self::SUCCESSFUL;
101
- return true;
102
  }
103
 
104
  /**
105
- * Returns the last status.
106
  *
107
- * @return string
108
  */
109
- public function last_status(): string {
110
-
111
- return $this->last_status;
112
  }
113
 
114
  /**
@@ -141,17 +157,6 @@ class AuthorizedPaymentsProcessor {
141
  return $authorizations;
142
  }
143
 
144
- /**
145
- * Whether Authorizations need to be captured.
146
- *
147
- * @param Authorization ...$authorizations All Authorizations.
148
- *
149
- * @return bool
150
- */
151
- private function are_authorzations_to_capture( Authorization ...$authorizations ): bool {
152
- return (bool) count( $this->authorizations_to_capture( ...$authorizations ) );
153
- }
154
-
155
  /**
156
  * Captures the authorizations.
157
  *
@@ -160,7 +165,7 @@ class AuthorizedPaymentsProcessor {
160
  private function capture_authorizations( Authorization ...$authorizations ) {
161
  $uncaptured_authorizations = $this->authorizations_to_capture( ...$authorizations );
162
  foreach ( $uncaptured_authorizations as $authorization ) {
163
- $this->payments_endpoint->capture( $authorization->id() );
164
  }
165
  }
166
 
@@ -171,11 +176,38 @@ class AuthorizedPaymentsProcessor {
171
  * @return Authorization[]
172
  */
173
  private function authorizations_to_capture( Authorization ...$authorizations ): array {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  return array_filter(
175
  $authorizations,
176
- static function ( Authorization $authorization ): bool {
177
- return $authorization->status()->is( AuthorizationStatus::CREATED )
178
- || $authorization->status()->is( AuthorizationStatus::PENDING );
179
  }
180
  );
181
  }
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
11
 
12
  use Exception;
13
+ use Psr\Log\LoggerInterface;
14
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
15
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
16
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
17
  use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
18
+ use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
19
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
20
  use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
21
 
24
  */
25
  class AuthorizedPaymentsProcessor {
26
 
27
+ use PaymentsStatusHandlingTrait;
28
+
29
+ const SUCCESSFUL = 'SUCCESSFUL';
30
+ const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
31
+ const FAILED = 'FAILED';
32
+ const INACCESSIBLE = 'INACCESSIBLE';
33
+ const NOT_FOUND = 'NOT_FOUND';
34
+ const BAD_AUTHORIZATION = 'BAD_AUTHORIZATION';
35
 
36
  /**
37
  * The Order endpoint.
48
  private $payments_endpoint;
49
 
50
  /**
51
+ * The logger.
52
+ *
53
+ * @var LoggerInterface
54
+ */
55
+ private $logger;
56
+
57
+ /**
58
+ * The capture results.
59
  *
60
+ * @var Capture[]
61
  */
62
+ private $captures;
63
 
64
  /**
65
  * AuthorizedPaymentsProcessor constructor.
66
  *
67
  * @param OrderEndpoint $order_endpoint The Order endpoint.
68
  * @param PaymentsEndpoint $payments_endpoint The Payments endpoint.
69
+ * @param LoggerInterface $logger The logger.
70
  */
71
  public function __construct(
72
  OrderEndpoint $order_endpoint,
73
+ PaymentsEndpoint $payments_endpoint,
74
+ LoggerInterface $logger
75
  ) {
76
 
77
  $this->order_endpoint = $order_endpoint;
78
  $this->payments_endpoint = $payments_endpoint;
79
+ $this->logger = $logger;
80
  }
81
 
82
  /**
84
  *
85
  * @param \WC_Order $wc_order The WooCommerce order.
86
  *
87
+ * @return string One of the AuthorizedPaymentsProcessor status constants.
88
  */
89
+ public function process( \WC_Order $wc_order ): string {
90
+ $this->captures = array();
91
+
92
  try {
93
  $order = $this->paypal_order_from_wc_order( $wc_order );
94
  } catch ( Exception $exception ) {
95
  if ( $exception->getCode() === 404 ) {
96
+ return self::NOT_FOUND;
 
97
  }
98
+ return self::INACCESSIBLE;
 
99
  }
100
 
101
  $authorizations = $this->all_authorizations( $order );
102
 
103
+ if ( ! $this->authorizations_to_capture( ...$authorizations ) ) {
104
+ if ( $this->captured_authorizations( ...$authorizations ) ) {
105
+ return self::ALREADY_CAPTURED;
106
+ }
107
+
108
+ return self::BAD_AUTHORIZATION;
109
  }
110
 
111
  try {
112
  $this->capture_authorizations( ...$authorizations );
113
  } catch ( Exception $exception ) {
114
+ $this->logger->error( 'Failed to capture authorization: ' . $exception->getMessage() );
115
+ return self::FAILED;
116
  }
117
 
118
+ return self::SUCCESSFUL;
 
119
  }
120
 
121
  /**
122
+ * Returns the capture results.
123
  *
124
+ * @return Capture[]
125
  */
126
+ public function captures(): array {
127
+ return $this->captures;
 
128
  }
129
 
130
  /**
157
  return $authorizations;
158
  }
159
 
 
 
 
 
 
 
 
 
 
 
 
160
  /**
161
  * Captures the authorizations.
162
  *
165
  private function capture_authorizations( Authorization ...$authorizations ) {
166
  $uncaptured_authorizations = $this->authorizations_to_capture( ...$authorizations );
167
  foreach ( $uncaptured_authorizations as $authorization ) {
168
+ $this->captures[] = $this->payments_endpoint->capture( $authorization->id() );
169
  }
170
  }
171
 
176
  * @return Authorization[]
177
  */
178
  private function authorizations_to_capture( Authorization ...$authorizations ): array {
179
+ return $this->filter_authorizations(
180
+ $authorizations,
181
+ array( AuthorizationStatus::CREATED, AuthorizationStatus::PENDING )
182
+ );
183
+ }
184
+
185
+ /**
186
+ * The authorizations which were captured.
187
+ *
188
+ * @param Authorization ...$authorizations All Authorizations.
189
+ * @return Authorization[]
190
+ */
191
+ private function captured_authorizations( Authorization ...$authorizations ): array {
192
+ return $this->filter_authorizations(
193
+ $authorizations,
194
+ array( AuthorizationStatus::CAPTURED )
195
+ );
196
+ }
197
+
198
+ /**
199
+ * The authorizations which need to be filtered.
200
+ *
201
+ * @param Authorization[] $authorizations All Authorizations.
202
+ * @param string[] $statuses Allowed statuses, the constants from AuthorizationStatus.
203
+ * @return Authorization[]
204
+ */
205
+ private function filter_authorizations( array $authorizations, array $statuses ): array {
206
  return array_filter(
207
  $authorizations,
208
+ static function ( Authorization $authorization ) use ( $statuses ): bool {
209
+ $status = $authorization->status();
210
+ return in_array( $status->name(), $statuses, true );
211
  }
212
  );
213
  }
modules/ppcp-wc-gateway/src/Processor/class-ordermetatrait.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Adds common metadata to the order.
4
+ *
5
+ * @package WooCommerce\PayPalCommerce\WcGateway\Processor
6
+ */
7
+
8
+ declare(strict_types=1);
9
+
10
+ namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
11
+
12
+ use WC_Order;
13
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
14
+ use WooCommerce\PayPalCommerce\Onboarding\Environment;
15
+ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
16
+
17
+ /**
18
+ * Trait OrderMetaTrait.
19
+ */
20
+ trait OrderMetaTrait {
21
+
22
+ /**
23
+ * Adds common metadata to the order.
24
+ *
25
+ * @param WC_Order $wc_order The WC order to which metadata will be added.
26
+ * @param Order $order The PayPal order.
27
+ * @param Environment $environment The environment.
28
+ */
29
+ protected function add_paypal_meta(
30
+ WC_Order $wc_order,
31
+ Order $order,
32
+ Environment $environment
33
+ ): void {
34
+ $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
35
+ $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
36
+ $wc_order->update_meta_data(
37
+ PayPalGateway::ORDER_PAYMENT_MODE_META_KEY,
38
+ $environment->current_environment_is( Environment::SANDBOX ) ? 'sandbox' : 'live'
39
+ );
40
+ }
41
+ }
modules/ppcp-wc-gateway/src/Processor/class-orderprocessor.php CHANGED
@@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
15
  use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
16
  use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
17
  use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
 
18
  use WooCommerce\PayPalCommerce\Session\SessionHandler;
19
  use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
20
  use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@@ -25,12 +26,14 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
25
  */
26
  class OrderProcessor {
27
 
 
 
28
  /**
29
- * Whether current payment mode is sandbox.
30
  *
31
- * @var bool
32
  */
33
- protected $sandbox_mode;
34
 
35
  /**
36
  * The payment token repository.
@@ -105,7 +108,7 @@ class OrderProcessor {
105
  * @param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor.
106
  * @param Settings $settings The Settings.
107
  * @param LoggerInterface $logger A logger service.
108
- * @param bool $sandbox_mode Whether sandbox mode enabled.
109
  */
110
  public function __construct(
111
  SessionHandler $session_handler,
@@ -115,7 +118,7 @@ class OrderProcessor {
115
  AuthorizedPaymentsProcessor $authorized_payments_processor,
116
  Settings $settings,
117
  LoggerInterface $logger,
118
- bool $sandbox_mode
119
  ) {
120
 
121
  $this->session_handler = $session_handler;
@@ -124,7 +127,7 @@ class OrderProcessor {
124
  $this->threed_secure = $three_d_secure;
125
  $this->authorized_payments_processor = $authorized_payments_processor;
126
  $this->settings = $settings;
127
- $this->sandbox_mode = $sandbox_mode;
128
  $this->logger = $logger;
129
  }
130
 
@@ -140,12 +143,8 @@ class OrderProcessor {
140
  if ( ! $order ) {
141
  return false;
142
  }
143
- $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
144
- $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
145
- $wc_order->update_meta_data(
146
- PayPalGateway::ORDER_PAYMENT_MODE_META_KEY,
147
- $this->sandbox_mode ? 'sandbox' : 'live'
148
- );
149
 
150
  $error_message = null;
151
  if ( ! $this->order_is_approved( $order ) ) {
@@ -164,12 +163,14 @@ class OrderProcessor {
164
  }
165
 
166
  $order = $this->patch_order( $wc_order, $order );
 
167
  if ( $order->intent() === 'CAPTURE' ) {
168
  $order = $this->order_endpoint->capture( $order );
169
  }
170
 
171
  if ( $order->intent() === 'AUTHORIZE' ) {
172
  $order = $this->order_endpoint->authorize( $order );
 
173
  $wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
174
  }
175
 
@@ -179,16 +180,9 @@ class OrderProcessor {
179
  $this->set_order_transaction_id( $transaction_id, $wc_order );
180
  }
181
 
182
- $wc_order->update_status(
183
- 'on-hold',
184
- __( 'Awaiting payment.', 'woocommerce-paypal-payments' )
185
- );
186
- if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'CAPTURE' ) {
187
-
188
- $wc_order->payment_complete();
189
- }
190
 
191
- if ( $this->capture_authorized_downloads( $order ) && $this->authorized_payments_processor->process( $wc_order ) ) {
192
  $wc_order->add_order_note(
193
  __( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
194
  );
15
  use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
16
  use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
17
  use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
18
+ use WooCommerce\PayPalCommerce\Onboarding\Environment;
19
  use WooCommerce\PayPalCommerce\Session\SessionHandler;
20
  use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
21
  use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
26
  */
27
  class OrderProcessor {
28
 
29
+ use OrderMetaTrait, PaymentsStatusHandlingTrait;
30
+
31
  /**
32
+ * The environment.
33
  *
34
+ * @var Environment
35
  */
36
+ protected $environment;
37
 
38
  /**
39
  * The payment token repository.
108
  * @param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor.
109
  * @param Settings $settings The Settings.
110
  * @param LoggerInterface $logger A logger service.
111
+ * @param Environment $environment The environment.
112
  */
113
  public function __construct(
114
  SessionHandler $session_handler,
118
  AuthorizedPaymentsProcessor $authorized_payments_processor,
119
  Settings $settings,
120
  LoggerInterface $logger,
121
+ Environment $environment
122
  ) {
123
 
124
  $this->session_handler = $session_handler;
127
  $this->threed_secure = $three_d_secure;
128
  $this->authorized_payments_processor = $authorized_payments_processor;
129
  $this->settings = $settings;
130
+ $this->environment = $environment;
131
  $this->logger = $logger;
132
  }
133
 
143
  if ( ! $order ) {
144
  return false;
145
  }
146
+
147
+ $this->add_paypal_meta( $wc_order, $order, $this->environment );
 
 
 
 
148
 
149
  $error_message = null;
150
  if ( ! $this->order_is_approved( $order ) ) {
163
  }
164
 
165
  $order = $this->patch_order( $wc_order, $order );
166
+
167
  if ( $order->intent() === 'CAPTURE' ) {
168
  $order = $this->order_endpoint->capture( $order );
169
  }
170
 
171
  if ( $order->intent() === 'AUTHORIZE' ) {
172
  $order = $this->order_endpoint->authorize( $order );
173
+
174
  $wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
175
  }
176
 
180
  $this->set_order_transaction_id( $transaction_id, $wc_order );
181
  }
182
 
183
+ $this->handle_new_order_status( $order, $wc_order );
 
 
 
 
 
 
 
184
 
185
+ if ( $this->capture_authorized_downloads( $order ) && AuthorizedPaymentsProcessor::SUCCESSFUL === $this->authorized_payments_processor->process( $wc_order ) ) {
186
  $wc_order->add_order_note(
187
  __( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
188
  );
modules/ppcp-wc-gateway/src/Processor/class-paymentstatushandlingtrait.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Common operations performed after payment authorization/capture.
4
+ *
5
+ * @package WooCommerce\PayPalCommerce\WcGateway\Processor
6
+ */
7
+
8
+ declare(strict_types=1);
9
+
10
+ namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
11
+
12
+ use WC_Order;
13
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
14
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
15
+ use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
16
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
17
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
18
+ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
19
+
20
+ /**
21
+ * Trait PaymentsStatusHandlingTrait.
22
+ */
23
+ trait PaymentsStatusHandlingTrait {
24
+
25
+ /**
26
+ * Changes status of a newly created order, based on the capture/authorization.
27
+ *
28
+ * @param Order $order The PayPal order.
29
+ * @param WC_Order $wc_order The WC order.
30
+ *
31
+ * @throws RuntimeException If payment denied.
32
+ */
33
+ protected function handle_new_order_status(
34
+ Order $order,
35
+ WC_Order $wc_order
36
+ ): void {
37
+ if ( $order->intent() === 'CAPTURE' ) {
38
+ $this->handle_capture_status( $order->purchase_units()[0]->payments()->captures()[0], $wc_order );
39
+ } elseif ( $order->intent() === 'AUTHORIZE' ) {
40
+ $this->handle_authorization_status( $order->purchase_units()[0]->payments()->authorizations()[0], $wc_order );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Changes the order status, based on the capture.
46
+ *
47
+ * @param Capture $capture The capture.
48
+ * @param WC_Order $wc_order The WC order.
49
+ *
50
+ * @throws RuntimeException If payment denied.
51
+ */
52
+ protected function handle_capture_status(
53
+ Capture $capture,
54
+ WC_Order $wc_order
55
+ ): void {
56
+ $status = $capture->status();
57
+
58
+ if ( $status->details() ) {
59
+ $this->add_status_details_note( $wc_order, $status->name(), $status->details()->text() );
60
+ }
61
+
62
+ switch ( $status->name() ) {
63
+ case CaptureStatus::COMPLETED:
64
+ $wc_order->payment_complete();
65
+ break;
66
+ // It is checked in the capture endpoint already, but there are other ways to capture,
67
+ // such as when paid via saved card.
68
+ case CaptureStatus::DECLINED:
69
+ $wc_order->update_status(
70
+ 'failed',
71
+ __( 'Could not capture the payment.', 'woocommerce-paypal-payments' )
72
+ );
73
+ throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
74
+ case CaptureStatus::PENDING:
75
+ case CaptureStatus::FAILED:
76
+ $wc_order->update_status(
77
+ 'on-hold',
78
+ __( 'Awaiting payment.', 'woocommerce-paypal-payments' )
79
+ );
80
+ break;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Changes the order status, based on the authorization.
86
+ *
87
+ * @param Authorization $authorization The authorization.
88
+ * @param WC_Order $wc_order The WC order.
89
+ *
90
+ * @throws RuntimeException If payment denied.
91
+ */
92
+ protected function handle_authorization_status(
93
+ Authorization $authorization,
94
+ WC_Order $wc_order
95
+ ): void {
96
+ $status = $authorization->status();
97
+
98
+ if ( $status->details() ) {
99
+ $this->add_status_details_note( $wc_order, $status->name(), $status->details()->text() );
100
+ }
101
+
102
+ switch ( $status->name() ) {
103
+ case AuthorizationStatus::CREATED:
104
+ case AuthorizationStatus::PENDING:
105
+ $wc_order->update_status(
106
+ 'on-hold',
107
+ __( 'Awaiting payment.', 'woocommerce-paypal-payments' )
108
+ );
109
+ break;
110
+ case AuthorizationStatus::DENIED:
111
+ $wc_order->update_status(
112
+ 'failed',
113
+ __( 'Could not get the payment authorization.', 'woocommerce-paypal-payments' )
114
+ );
115
+ throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Adds the order note with status details.
121
+ *
122
+ * @param WC_Order $wc_order The WC order to which the note will be added.
123
+ * @param string $status The status name.
124
+ * @param string $reason The status reason.
125
+ */
126
+ protected function add_status_details_note(
127
+ WC_Order $wc_order,
128
+ string $status,
129
+ string $reason
130
+ ): void {
131
+ $wc_order->add_order_note(
132
+ sprintf(
133
+ /* translators: %1$s - PENDING, DENIED, ... %2$s - text explaining the reason, ... */
134
+ __( 'PayPal order payment is set to %1$s status, details: %2$s', 'woocommerce-paypal-payments' ),
135
+ $status,
136
+ $reason
137
+ )
138
+ );
139
+ $wc_order->save();
140
+ }
141
+ }
modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php CHANGED
@@ -9,10 +9,15 @@ declare( strict_types=1 );
9
 
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
11
 
 
 
12
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
13
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
14
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
 
 
15
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
 
16
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
17
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
18
  use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@@ -22,6 +27,10 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
22
  */
23
  class RefundProcessor {
24
 
 
 
 
 
25
  /**
26
  * The order endpoint.
27
  *
@@ -36,16 +45,25 @@ class RefundProcessor {
36
  */
37
  private $payments_endpoint;
38
 
 
 
 
 
 
 
 
39
  /**
40
  * RefundProcessor constructor.
41
  *
42
  * @param OrderEndpoint $order_endpoint The order endpoint.
43
  * @param PaymentsEndpoint $payments_endpoint The payments endpoint.
 
44
  */
45
- public function __construct( OrderEndpoint $order_endpoint, PaymentsEndpoint $payments_endpoint ) {
46
 
47
  $this->order_endpoint = $order_endpoint;
48
  $this->payments_endpoint = $payments_endpoint;
 
49
  }
50
 
51
  /**
@@ -56,44 +74,116 @@ class RefundProcessor {
56
  * @param string $reason The reason for the refund.
57
  *
58
  * @return bool
 
 
59
  */
60
  public function process( \WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool {
61
- $order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
62
- if ( ! $order_id ) {
63
- return false;
64
- }
65
  try {
66
- $order = $this->order_endpoint->order( $order_id );
67
- if ( ! $order ) {
68
- return false;
69
  }
70
 
 
 
71
  $purchase_units = $order->purchase_units();
72
  if ( ! $purchase_units ) {
73
- return false;
74
  }
75
 
76
  $payments = $purchase_units[0]->payments();
77
  if ( ! $payments ) {
78
- return false;
79
- }
80
- $captures = $payments->captures();
81
- if ( ! $captures ) {
82
- return false;
83
  }
84
 
85
- $capture = $captures[0];
86
- $refund = new Refund(
87
- $capture,
88
- $capture->invoice_id(),
89
- $reason,
90
- new Amount(
91
- new Money( $amount, $wc_order->get_currency() )
92
  )
93
  );
94
- return $this->payments_endpoint->refund( $refund );
95
- } catch ( RuntimeException $error ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  return false;
97
  }
98
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
9
 
10
  namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
11
 
12
+ use Exception;
13
+ use Psr\Log\LoggerInterface;
14
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
15
  use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
16
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
17
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
18
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
19
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
20
+ use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments;
21
  use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
22
  use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
23
  use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
27
  */
28
  class RefundProcessor {
29
 
30
+ private const REFUND_MODE_REFUND = 'refund';
31
+ private const REFUND_MODE_VOID = 'void';
32
+ private const REFUND_MODE_UNKNOWN = 'unknown';
33
+
34
  /**
35
  * The order endpoint.
36
  *
45
  */
46
  private $payments_endpoint;
47
 
48
+ /**
49
+ * The logger.
50
+ *
51
+ * @var LoggerInterface
52
+ */
53
+ private $logger;
54
+
55
  /**
56
  * RefundProcessor constructor.
57
  *
58
  * @param OrderEndpoint $order_endpoint The order endpoint.
59
  * @param PaymentsEndpoint $payments_endpoint The payments endpoint.
60
+ * @param LoggerInterface $logger The logger.
61
  */
62
+ public function __construct( OrderEndpoint $order_endpoint, PaymentsEndpoint $payments_endpoint, LoggerInterface $logger ) {
63
 
64
  $this->order_endpoint = $order_endpoint;
65
  $this->payments_endpoint = $payments_endpoint;
66
+ $this->logger = $logger;
67
  }
68
 
69
  /**
74
  * @param string $reason The reason for the refund.
75
  *
76
  * @return bool
77
+ *
78
+ * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.Missing
79
  */
80
  public function process( \WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool {
 
 
 
 
81
  try {
82
+ $order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
83
+ if ( ! $order_id ) {
84
+ throw new RuntimeException( 'PayPal order ID not found in meta.' );
85
  }
86
 
87
+ $order = $this->order_endpoint->order( $order_id );
88
+
89
  $purchase_units = $order->purchase_units();
90
  if ( ! $purchase_units ) {
91
+ throw new RuntimeException( 'No purchase units.' );
92
  }
93
 
94
  $payments = $purchase_units[0]->payments();
95
  if ( ! $payments ) {
96
+ throw new RuntimeException( 'No payments.' );
 
 
 
 
97
  }
98
 
99
+ $this->logger->debug(
100
+ sprintf(
101
+ 'Trying to refund/void order %1$s, payments: %2$s.',
102
+ $order->id(),
103
+ wp_json_encode( $payments->to_array() )
 
 
104
  )
105
  );
106
+
107
+ $mode = $this->determine_refund_mode( $payments );
108
+
109
+ switch ( $mode ) {
110
+ case self::REFUND_MODE_REFUND:
111
+ $captures = $payments->captures();
112
+ if ( ! $captures ) {
113
+ throw new RuntimeException( 'No capture.' );
114
+ }
115
+
116
+ $capture = $captures[0];
117
+ $refund = new Refund(
118
+ $capture,
119
+ $capture->invoice_id(),
120
+ $reason,
121
+ new Amount(
122
+ new Money( $amount, $wc_order->get_currency() )
123
+ )
124
+ );
125
+ $this->payments_endpoint->refund( $refund );
126
+ break;
127
+ case self::REFUND_MODE_VOID:
128
+ $voidable_authorizations = array_filter(
129
+ $payments->authorizations(),
130
+ array( $this, 'is_voidable_authorization' )
131
+ );
132
+ if ( ! $voidable_authorizations ) {
133
+ throw new RuntimeException( 'No voidable authorizations.' );
134
+ }
135
+
136
+ foreach ( $voidable_authorizations as $authorization ) {
137
+ $this->payments_endpoint->void( $authorization );
138
+ }
139
+
140
+ $wc_order->set_status( 'refunded' );
141
+ $wc_order->save();
142
+
143
+ break;
144
+ default:
145
+ throw new RuntimeException( 'Nothing to refund/void.' );
146
+ }
147
+
148
+ return true;
149
+ } catch ( Exception $error ) {
150
+ $this->logger->error( 'Refund failed: ' . $error->getMessage() );
151
  return false;
152
  }
153
  }
154
+
155
+ /**
156
+ * Determines the refunding mode.
157
+ *
158
+ * @param Payments $payments The order payments state.
159
+ *
160
+ * @return string One of the REFUND_MODE_ constants.
161
+ */
162
+ private function determine_refund_mode( Payments $payments ): string {
163
+ $authorizations = $payments->authorizations();
164
+ if ( $authorizations ) {
165
+ foreach ( $authorizations as $authorization ) {
166
+ if ( $this->is_voidable_authorization( $authorization ) ) {
167
+ return self::REFUND_MODE_VOID;
168
+ }
169
+ }
170
+ }
171
+
172
+ if ( $payments->captures() ) {
173
+ return self::REFUND_MODE_REFUND;
174
+ }
175
+
176
+ return self::REFUND_MODE_UNKNOWN;
177
+ }
178
+
179
+ /**
180
+ * Checks whether the authorization can be voided.
181
+ *
182
+ * @param Authorization $authorization The authorization to check.
183
+ * @return bool
184
+ */
185
+ private function is_voidable_authorization( Authorization $authorization ): bool {
186
+ return $authorization->status()->is( AuthorizationStatus::CREATED ) ||
187
+ $authorization->status()->is( AuthorizationStatus::PENDING );
188
+ }
189
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, e-commerce, store, sales, sell,
4
  Requires at least: 5.3
5
  Tested up to: 5.8
6
  Requires PHP: 7.1
7
- Stable tag: 1.6.0
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -81,6 +81,13 @@ Follow the steps below to connect the plugin to your PayPal account:
81
 
82
  == Changelog ==
83
 
 
 
 
 
 
 
 
84
  = 1.6.0 =
85
  * Add - Webhook status. #246 #273
86
  * Add - Show CC gateway in admin payments list. #236
4
  Requires at least: 5.3
5
  Tested up to: 5.8
6
  Requires PHP: 7.1
7
+ Stable tag: 1.6.1
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
81
 
82
  == Changelog ==
83
 
84
+ = 1.6.1 =
85
+ * Fix - Handle authorization capture failures #312
86
+ * Fix - Handle denied payment authorization #302
87
+ * Fix - Handle failed authorizations when capturing order #303
88
+ * Fix - Transactions cannot be voided #293
89
+ * Fix - Fatal error: get_3ds_contingency() #310
90
+
91
  = 1.6.0 =
92
  * Add - Webhook status. #246 #273
93
  * Add - Show CC gateway in admin payments list. #236
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit6f3e12b28f1ed670640881dfaf1b748a::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit4607be6ea28f9ff73f3cf09747918e4b::getLoader();
vendor/composer/autoload_classmap.php CHANGED
@@ -32,6 +32,9 @@ return array(
32
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\ApplicationContext' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-applicationcontext.php',
33
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Authorization' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-authorization.php',
34
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\AuthorizationStatus' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-authorizationstatus.php',
 
 
 
35
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CardAuthenticationResult' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-cardauthenticationresult.php',
36
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Item' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-item.php',
37
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Money' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-money.php',
@@ -149,7 +152,9 @@ return array(
149
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\ConnectAdminNotice' => $baseDir . '/modules/ppcp-wc-gateway/src/Notice/class-connectadminnotice.php',
150
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\DccWithoutPayPalAdminNotice' => $baseDir . '/modules/ppcp-wc-gateway/src/Notice/class-dccwithoutpaypaladminnotice.php',
151
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\AuthorizedPaymentsProcessor' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-authorizedpaymentsprocessor.php',
 
152
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\OrderProcessor' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-orderprocessor.php',
 
153
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\RefundProcessor' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php',
154
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\PageMatcherTrait' => $baseDir . '/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php',
155
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\SectionsRenderer' => $baseDir . '/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php',
32
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\ApplicationContext' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-applicationcontext.php',
33
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Authorization' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-authorization.php',
34
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\AuthorizationStatus' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-authorizationstatus.php',
35
+ 'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\AuthorizationStatusDetails' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-authorizationstatusdetails.php',
36
+ 'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CaptureStatus' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-capturestatus.php',
37
+ 'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CaptureStatusDetails' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-capturestatusdetails.php',
38
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CardAuthenticationResult' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-cardauthenticationresult.php',
39
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Item' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-item.php',
40
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Money' => $baseDir . '/modules/ppcp-api-client/src/Entity/class-money.php',
152
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\ConnectAdminNotice' => $baseDir . '/modules/ppcp-wc-gateway/src/Notice/class-connectadminnotice.php',
153
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\DccWithoutPayPalAdminNotice' => $baseDir . '/modules/ppcp-wc-gateway/src/Notice/class-dccwithoutpaypaladminnotice.php',
154
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\AuthorizedPaymentsProcessor' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-authorizedpaymentsprocessor.php',
155
+ 'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\OrderMetaTrait' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-ordermetatrait.php',
156
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\OrderProcessor' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-orderprocessor.php',
157
+ 'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\PaymentsStatusHandlingTrait' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-paymentstatushandlingtrait.php',
158
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\RefundProcessor' => $baseDir . '/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php',
159
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\PageMatcherTrait' => $baseDir . '/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php',
160
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\SectionsRenderer' => $baseDir . '/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php',
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit6f3e12b28f1ed670640881dfaf1b748a
6
  {
7
  private static $loader;
8
 
@@ -24,15 +24,15 @@ class ComposerAutoloaderInit6f3e12b28f1ed670640881dfaf1b748a
24
 
25
  require __DIR__ . '/platform_check.php';
26
 
27
- spl_autoload_register(array('ComposerAutoloaderInit6f3e12b28f1ed670640881dfaf1b748a', 'loadClassLoader'), true, true);
28
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29
- spl_autoload_unregister(array('ComposerAutoloaderInit6f3e12b28f1ed670640881dfaf1b748a', 'loadClassLoader'));
30
 
31
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
  if ($useStaticLoader) {
33
  require __DIR__ . '/autoload_static.php';
34
 
35
- call_user_func(\Composer\Autoload\ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a::getInitializer($loader));
36
  } else {
37
  $map = require __DIR__ . '/autoload_namespaces.php';
38
  foreach ($map as $namespace => $path) {
@@ -53,19 +53,19 @@ class ComposerAutoloaderInit6f3e12b28f1ed670640881dfaf1b748a
53
  $loader->register(true);
54
 
55
  if ($useStaticLoader) {
56
- $includeFiles = Composer\Autoload\ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a::$files;
57
  } else {
58
  $includeFiles = require __DIR__ . '/autoload_files.php';
59
  }
60
  foreach ($includeFiles as $fileIdentifier => $file) {
61
- composerRequire6f3e12b28f1ed670640881dfaf1b748a($fileIdentifier, $file);
62
  }
63
 
64
  return $loader;
65
  }
66
  }
67
 
68
- function composerRequire6f3e12b28f1ed670640881dfaf1b748a($fileIdentifier, $file)
69
  {
70
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
71
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit4607be6ea28f9ff73f3cf09747918e4b
6
  {
7
  private static $loader;
8
 
24
 
25
  require __DIR__ . '/platform_check.php';
26
 
27
+ spl_autoload_register(array('ComposerAutoloaderInit4607be6ea28f9ff73f3cf09747918e4b', 'loadClassLoader'), true, true);
28
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29
+ spl_autoload_unregister(array('ComposerAutoloaderInit4607be6ea28f9ff73f3cf09747918e4b', 'loadClassLoader'));
30
 
31
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
  if ($useStaticLoader) {
33
  require __DIR__ . '/autoload_static.php';
34
 
35
+ call_user_func(\Composer\Autoload\ComposerStaticInit4607be6ea28f9ff73f3cf09747918e4b::getInitializer($loader));
36
  } else {
37
  $map = require __DIR__ . '/autoload_namespaces.php';
38
  foreach ($map as $namespace => $path) {
53
  $loader->register(true);
54
 
55
  if ($useStaticLoader) {
56
+ $includeFiles = Composer\Autoload\ComposerStaticInit4607be6ea28f9ff73f3cf09747918e4b::$files;
57
  } else {
58
  $includeFiles = require __DIR__ . '/autoload_files.php';
59
  }
60
  foreach ($includeFiles as $fileIdentifier => $file) {
61
+ composerRequire4607be6ea28f9ff73f3cf09747918e4b($fileIdentifier, $file);
62
  }
63
 
64
  return $loader;
65
  }
66
  }
67
 
68
+ function composerRequire4607be6ea28f9ff73f3cf09747918e4b($fileIdentifier, $file)
69
  {
70
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
71
  require $file;
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a
8
  {
9
  public static $files = array (
10
  '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
@@ -107,6 +107,9 @@ class ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a
107
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\ApplicationContext' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-applicationcontext.php',
108
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Authorization' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-authorization.php',
109
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\AuthorizationStatus' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-authorizationstatus.php',
 
 
 
110
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CardAuthenticationResult' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-cardauthenticationresult.php',
111
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Item' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-item.php',
112
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Money' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-money.php',
@@ -224,7 +227,9 @@ class ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a
224
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\ConnectAdminNotice' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Notice/class-connectadminnotice.php',
225
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\DccWithoutPayPalAdminNotice' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Notice/class-dccwithoutpaypaladminnotice.php',
226
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\AuthorizedPaymentsProcessor' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-authorizedpaymentsprocessor.php',
 
227
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\OrderProcessor' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-orderprocessor.php',
 
228
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\RefundProcessor' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php',
229
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\PageMatcherTrait' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php',
230
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\SectionsRenderer' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php',
@@ -258,9 +263,9 @@ class ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a
258
  public static function getInitializer(ClassLoader $loader)
259
  {
260
  return \Closure::bind(function () use ($loader) {
261
- $loader->prefixLengthsPsr4 = ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a::$prefixLengthsPsr4;
262
- $loader->prefixDirsPsr4 = ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a::$prefixDirsPsr4;
263
- $loader->classMap = ComposerStaticInit6f3e12b28f1ed670640881dfaf1b748a::$classMap;
264
 
265
  }, null, ClassLoader::class);
266
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit4607be6ea28f9ff73f3cf09747918e4b
8
  {
9
  public static $files = array (
10
  '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
107
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\ApplicationContext' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-applicationcontext.php',
108
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Authorization' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-authorization.php',
109
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\AuthorizationStatus' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-authorizationstatus.php',
110
+ 'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\AuthorizationStatusDetails' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-authorizationstatusdetails.php',
111
+ 'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CaptureStatus' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-capturestatus.php',
112
+ 'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CaptureStatusDetails' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-capturestatusdetails.php',
113
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\CardAuthenticationResult' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-cardauthenticationresult.php',
114
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Item' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-item.php',
115
  'WooCommerce\\PayPalCommerce\\ApiClient\\Entity\\Money' => __DIR__ . '/../..' . '/modules/ppcp-api-client/src/Entity/class-money.php',
227
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\ConnectAdminNotice' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Notice/class-connectadminnotice.php',
228
  'WooCommerce\\PayPalCommerce\\WcGateway\\Notice\\DccWithoutPayPalAdminNotice' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Notice/class-dccwithoutpaypaladminnotice.php',
229
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\AuthorizedPaymentsProcessor' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-authorizedpaymentsprocessor.php',
230
+ 'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\OrderMetaTrait' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-ordermetatrait.php',
231
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\OrderProcessor' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-orderprocessor.php',
232
+ 'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\PaymentsStatusHandlingTrait' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-paymentstatushandlingtrait.php',
233
  'WooCommerce\\PayPalCommerce\\WcGateway\\Processor\\RefundProcessor' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php',
234
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\PageMatcherTrait' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php',
235
  'WooCommerce\\PayPalCommerce\\WcGateway\\Settings\\SectionsRenderer' => __DIR__ . '/../..' . '/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php',
263
  public static function getInitializer(ClassLoader $loader)
264
  {
265
  return \Closure::bind(function () use ($loader) {
266
+ $loader->prefixLengthsPsr4 = ComposerStaticInit4607be6ea28f9ff73f3cf09747918e4b::$prefixLengthsPsr4;
267
+ $loader->prefixDirsPsr4 = ComposerStaticInit4607be6ea28f9ff73f3cf09747918e4b::$prefixDirsPsr4;
268
+ $loader->classMap = ComposerStaticInit4607be6ea28f9ff73f3cf09747918e4b::$classMap;
269
 
270
  }, null, ClassLoader::class);
271
  }
vendor/composer/installed.php CHANGED
@@ -5,7 +5,7 @@
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
- 'reference' => '505f5223cef9205a77df95f00362e3f515ae8666',
9
  'name' => 'woocommerce/woocommerce-paypal-payments',
10
  'dev' => false,
11
  ),
@@ -124,7 +124,7 @@
124
  'type' => 'wordpress-plugin',
125
  'install_path' => __DIR__ . '/../../',
126
  'aliases' => array(),
127
- 'reference' => '505f5223cef9205a77df95f00362e3f515ae8666',
128
  'dev_requirement' => false,
129
  ),
130
  ),
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
+ 'reference' => 'c0a727e4a85ecf1e51b60a26f371a7cb94f7e6e0',
9
  'name' => 'woocommerce/woocommerce-paypal-payments',
10
  'dev' => false,
11
  ),
124
  'type' => 'wordpress-plugin',
125
  'install_path' => __DIR__ . '/../../',
126
  'aliases' => array(),
127
+ 'reference' => 'c0a727e4a85ecf1e51b60a26f371a7cb94f7e6e0',
128
  'dev_requirement' => false,
129
  ),
130
  ),
woocommerce-paypal-payments.php CHANGED
@@ -3,13 +3,13 @@
3
  * Plugin Name: WooCommerce PayPal Payments
4
  * Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
5
  * Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
6
- * Version: 1.6.0
7
  * Author: WooCommerce
8
  * Author URI: https://woocommerce.com/
9
  * License: GPL-2.0
10
  * Requires PHP: 7.1
11
  * WC requires at least: 3.9
12
- * WC tested up to: 5.6
13
  * Text Domain: woocommerce-paypal-payments
14
  *
15
  * @package WooCommerce\PayPalCommerce
3
  * Plugin Name: WooCommerce PayPal Payments
4
  * Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
5
  * Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
6
+ * Version: 1.6.1
7
  * Author: WooCommerce
8
  * Author URI: https://woocommerce.com/
9
  * License: GPL-2.0
10
  * Requires PHP: 7.1
11
  * WC requires at least: 3.9
12
+ * WC tested up to: 5.7
13
  * Text Domain: woocommerce-paypal-payments
14
  *
15
  * @package WooCommerce\PayPalCommerce