mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-03 08:37:53 +08:00
Merge branch 'trunk' into PCP-782-resubscribe-webhooks-on-plugin-upgrades
This commit is contained in:
commit
2910d22f0b
47 changed files with 5567 additions and 2576 deletions
11
modules/ppcp-webhooks/.babelrc
Normal file
11
modules/ppcp-webhooks/.babelrc
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.25.0"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
|
@ -1,23 +1,32 @@
|
|||
{
|
||||
"name": "ppcp-webhooks",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"main": "resources/js/status-page.js",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"webpack": "^5.55.0",
|
||||
"webpack-cli": "^4.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
"name": "ppcp-webhooks",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"main": "resources/js/status-page.js",
|
||||
"browserslist": [
|
||||
"> 0.5%",
|
||||
"Safari >= 8",
|
||||
"Chrome >= 41",
|
||||
"Firefox >= 43",
|
||||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"core-js": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19",
|
||||
"@babel/preset-env": "^7.19",
|
||||
"babel-loader": "^8.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"webpack": "^5.74",
|
||||
"webpack-cli": "^4.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ document.addEventListener(
|
|||
PayPalCommerceGatewayWebhooksStatus.resubscribe.endpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
|
@ -52,6 +53,7 @@ document.addEventListener(
|
|||
PayPalCommerceGatewayWebhooksStatus.simulation.start.endpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
|
@ -106,6 +108,7 @@ document.addEventListener(
|
|||
PayPalCommerceGatewayWebhooksStatus.simulation.state.endpoint,
|
||||
{
|
||||
method: 'GET',
|
||||
credentials: 'same-origin',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulationStateEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutOrderApproved;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutOrderCompleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutPaymentApprovalReversed;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureCompleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureDenied;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCapturePending;
|
||||
|
@ -75,6 +76,7 @@ return array(
|
|||
return array(
|
||||
new CheckoutOrderApproved( $logger, $prefix, $order_endpoint ),
|
||||
new CheckoutOrderCompleted( $logger, $prefix ),
|
||||
new CheckoutPaymentApprovalReversed( $logger ),
|
||||
new PaymentCaptureRefunded( $logger, $prefix ),
|
||||
new PaymentCaptureReversed( $logger, $prefix ),
|
||||
new PaymentCaptureCompleted( $logger, $prefix, $order_endpoint ),
|
||||
|
|
|
@ -11,13 +11,15 @@ namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
|||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class CheckoutOrderCompleted
|
||||
*/
|
||||
class CheckoutOrderCompleted implements RequestHandler {
|
||||
|
||||
use PrefixTrait;
|
||||
use PrefixTrait, RequestHandlerTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
|
@ -51,97 +53,45 @@ class CheckoutOrderCompleted implements RequestHandler {
|
|||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param \WP_REST_Request $request The request.
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( \WP_REST_Request $request ): bool {
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param \WP_REST_Request $request The request.
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return \WP_REST_Response
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( \WP_REST_Request $request ): \WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
$custom_ids = array_filter(
|
||||
array_map(
|
||||
static function ( array $purchase_unit ): string {
|
||||
return isset( $purchase_unit['custom_id'] ) ?
|
||||
(string) $purchase_unit['custom_id'] : '';
|
||||
},
|
||||
isset( $request['resource']['purchase_units'] ) ?
|
||||
(array) $request['resource']['purchase_units'] : array()
|
||||
),
|
||||
static function ( string $order_id ): bool {
|
||||
return ! empty( $order_id );
|
||||
}
|
||||
);
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
|
||||
$custom_ids = $this->get_custom_ids_from_request( $request );
|
||||
if ( empty( $custom_ids ) ) {
|
||||
$message = sprintf(
|
||||
// translators: %s is the PayPal webhook Id.
|
||||
__(
|
||||
'No order for webhook event %s was found.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
isset( $request['id'] ) ? $request['id'] : ''
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$message,
|
||||
array(
|
||||
'request' => $request,
|
||||
)
|
||||
);
|
||||
$response['message'] = $message;
|
||||
return rest_ensure_response( $response );
|
||||
return $this->no_custom_ids_from_request( $request, $response );
|
||||
}
|
||||
|
||||
$order_ids = array_map(
|
||||
array(
|
||||
$this,
|
||||
'sanitize_custom_id',
|
||||
),
|
||||
$custom_ids
|
||||
);
|
||||
$args = array(
|
||||
'post__in' => $order_ids,
|
||||
'limit' => -1,
|
||||
);
|
||||
$wc_orders = wc_get_orders( $args );
|
||||
$wc_orders = $this->get_wc_orders_from_custom_ids( $custom_ids );
|
||||
if ( ! $wc_orders ) {
|
||||
$message = sprintf(
|
||||
// translators: %s is the PayPal order Id.
|
||||
__( 'Order for PayPal order %s not found.', 'woocommerce-paypal-payments' ),
|
||||
isset( $request['resource']['id'] ) ? $request['resource']['id'] : ''
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$message,
|
||||
array(
|
||||
'request' => $request,
|
||||
)
|
||||
);
|
||||
$response['message'] = $message;
|
||||
return rest_ensure_response( $response );
|
||||
return $this->no_wc_orders_from_custom_ids( $request, $response );
|
||||
}
|
||||
|
||||
foreach ( $wc_orders as $wc_order ) {
|
||||
if ( PayUponInvoiceGateway::ID === $wc_order->get_payment_method() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! in_array( $wc_order->get_status(), array( 'pending', 'on-hold' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wc_order->payment_complete();
|
||||
$this->logger->log(
|
||||
'info',
|
||||
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
// translators: %s is the order ID.
|
||||
__(
|
||||
|
@ -149,15 +99,11 @@ class CheckoutOrderCompleted implements RequestHandler {
|
|||
'woocommerce-paypal-payments'
|
||||
),
|
||||
(string) $wc_order->get_id()
|
||||
),
|
||||
array(
|
||||
'request' => $request,
|
||||
'order' => $wc_order,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return rest_ensure_response( $response );
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
// phpcs:enable Inpsyde.CodeQuality.FunctionLength.TooLong
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles CHECKOUT.PAYMENT-APPROVAL.REVERSED Webhook.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class CheckoutPaymentApprovalReversed
|
||||
*/
|
||||
class CheckoutPaymentApprovalReversed implements RequestHandler {
|
||||
|
||||
use RequestHandlerTrait, PrefixTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* CheckoutPaymentApprovalReversed constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array(
|
||||
'CHECKOUT.PAYMENT-APPROVAL.REVERSED',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
|
||||
$custom_ids = $this->get_custom_ids_from_request( $request );
|
||||
if ( empty( $custom_ids ) ) {
|
||||
return $this->no_custom_ids_from_request( $request, $response );
|
||||
}
|
||||
|
||||
$wc_orders = $this->get_wc_orders_from_custom_ids( $custom_ids );
|
||||
if ( ! $wc_orders ) {
|
||||
return $this->no_wc_orders_from_custom_ids( $request, $response );
|
||||
}
|
||||
|
||||
foreach ( $wc_orders as $wc_order ) {
|
||||
if ( in_array( $wc_order->get_status(), array( 'pending', 'on-hold' ), true ) ) {
|
||||
$error_message = sprintf(
|
||||
// translators: %1$s is the order id.
|
||||
__(
|
||||
'Failed to capture order %1$s through PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
(string) $wc_order->get_id()
|
||||
);
|
||||
|
||||
$this->logger->warning( 'CHECKOUT.PAYMENT-APPROVAL.REVERSED received. ' . $error_message );
|
||||
|
||||
$wc_order->update_status( 'failed', $error_message );
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
111
modules/ppcp-webhooks/src/Handler/RequestHandlerTrait.php
Normal file
111
modules/ppcp-webhooks/src/Handler/RequestHandlerTrait.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
/**
|
||||
* Trait which helps to handle the request.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use stdClass;
|
||||
use WC_Order;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
trait RequestHandlerTrait {
|
||||
|
||||
/**
|
||||
* Get available custom ids from the given request
|
||||
*
|
||||
* @param \WP_REST_Request $request The request.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_custom_ids_from_request( WP_REST_Request $request ): array {
|
||||
return array_filter(
|
||||
array_map(
|
||||
static function ( array $purchase_unit ): string {
|
||||
return isset( $purchase_unit['custom_id'] ) ?
|
||||
(string) $purchase_unit['custom_id'] : '';
|
||||
},
|
||||
$request['resource'] !== null && isset( $request['resource']['purchase_units'] ) ?
|
||||
(array) $request['resource']['purchase_units'] : array()
|
||||
),
|
||||
static function ( string $order_id ): bool {
|
||||
return ! empty( $order_id );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WC orders from the given custom ids.
|
||||
*
|
||||
* @param array $custom_ids The custom ids.
|
||||
* @return WC_Order[]
|
||||
*/
|
||||
protected function get_wc_orders_from_custom_ids( array $custom_ids ): array {
|
||||
$order_ids = array_map(
|
||||
array(
|
||||
$this,
|
||||
'sanitize_custom_id',
|
||||
),
|
||||
$custom_ids
|
||||
);
|
||||
$args = array(
|
||||
'post__in' => $order_ids,
|
||||
'limit' => -1,
|
||||
);
|
||||
|
||||
$orders = wc_get_orders( $args );
|
||||
return is_array( $orders ) ? $orders : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return and log response for no custom ids found in request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
* @param array $response The response.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
protected function no_custom_ids_from_request( WP_REST_Request $request, array $response ): WP_REST_Response {
|
||||
$message = sprintf(
|
||||
// translators: %s is the PayPal webhook Id.
|
||||
__( 'No order for webhook event %s was found.', 'woocommerce-paypal-payments' ),
|
||||
$request['id'] !== null && isset( $request['id'] ) ? $request['id'] : ''
|
||||
);
|
||||
|
||||
return $this->log_and_return_response( $message, $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return and log response for no WC orders found in response.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
* @param array $response The response.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
protected function no_wc_orders_from_custom_ids( WP_REST_Request $request, array $response ): WP_REST_Response {
|
||||
$message = sprintf(
|
||||
// translators: %s is the PayPal order Id.
|
||||
__( 'WC order for PayPal order %s not found.', 'woocommerce-paypal-payments' ),
|
||||
$request['resource'] !== null && isset( $request['resource']['id'] ) ? $request['resource']['id'] : ''
|
||||
);
|
||||
|
||||
return $this->log_and_return_response( $message, $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return and log response with the given message.
|
||||
*
|
||||
* @param string $message The message.
|
||||
* @param array $response The response.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
private function log_and_return_response( string $message, array $response ): WP_REST_Response {
|
||||
$this->logger->warning( $message );
|
||||
$response['message'] = $message;
|
||||
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ const path = require('path');
|
|||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
module.exports = {
|
||||
devtool: 'eval-source-map',
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'web',
|
||||
entry: {
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue