Merge remote-tracking branch 'origin/trunk' into modularity-module-migration

# Conflicts:
#	modules/ppcp-admin-notices/src/AdminNotices.php
#	modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
#	modules/ppcp-wc-gateway/src/WCGatewayModule.php
#	modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
#	tests/PHPUnit/WcSubscriptions/RenewalHandlerTest.php
This commit is contained in:
Pedro Silva 2024-03-27 18:42:21 +00:00
commit 392d27f08b
No known key found for this signature in database
GPG key ID: E2EE20C0669D24B3
163 changed files with 13687 additions and 1746 deletions

View file

@ -28,6 +28,14 @@ document.addEventListener(
init()
});
if(ppcp_add_payment_method.is_subscription_change_payment_page) {
const saveToAccount = document.querySelector('#wc-ppcp-credit-card-gateway-new-payment-method');
if(saveToAccount) {
saveToAccount.checked = true;
saveToAccount.disabled = true;
}
}
setTimeout(() => {
loadScript({
clientId: ppcp_add_payment_method.client_id,
@ -38,54 +46,57 @@ document.addEventListener(
.then((paypal) => {
errorHandler.clear();
paypal.Buttons(
{
createVaultSetupToken: async () => {
const response = await fetch(ppcp_add_payment_method.ajax.create_setup_token.endpoint, {
method: "POST",
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nonce: ppcp_add_payment_method.ajax.create_setup_token.nonce,
const paypalButtonContainer = document.querySelector(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`);
if(paypalButtonContainer) {
paypal.Buttons(
{
createVaultSetupToken: async () => {
const response = await fetch(ppcp_add_payment_method.ajax.create_setup_token.endpoint, {
method: "POST",
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nonce: ppcp_add_payment_method.ajax.create_setup_token.nonce,
})
})
})
const result = await response.json()
if (result.data.id) {
return result.data.id
}
const result = await response.json()
if (result.data.id) {
return result.data.id
}
errorHandler.message(ppcp_add_payment_method.error_message);
},
onApprove: async ({vaultSetupToken}) => {
const response = await fetch(ppcp_add_payment_method.ajax.create_payment_token.endpoint, {
method: "POST",
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nonce: ppcp_add_payment_method.ajax.create_payment_token.nonce,
vault_setup_token: vaultSetupToken,
errorHandler.message(ppcp_add_payment_method.error_message);
},
onApprove: async ({vaultSetupToken}) => {
const response = await fetch(ppcp_add_payment_method.ajax.create_payment_token.endpoint, {
method: "POST",
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nonce: ppcp_add_payment_method.ajax.create_payment_token.nonce,
vault_setup_token: vaultSetupToken,
})
})
})
const result = await response.json();
if(result.success === true) {
window.location.href = ppcp_add_payment_method.payment_methods_page;
return;
const result = await response.json();
if(result.success === true) {
window.location.href = ppcp_add_payment_method.payment_methods_page;
return;
}
errorHandler.message(ppcp_add_payment_method.error_message);
},
onError: (error) => {
console.error(error)
errorHandler.message(ppcp_add_payment_method.error_message);
}
errorHandler.message(ppcp_add_payment_method.error_message);
},
onError: (error) => {
console.error(error)
errorHandler.message(ppcp_add_payment_method.error_message);
}
},
).render(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`);
).render(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`);
}
const cardField = paypal.CardFields({
createVaultSetupToken: async () => {
@ -125,6 +136,33 @@ document.addEventListener(
const result = await response.json();
if(result.success === true) {
if(ppcp_add_payment_method.is_subscription_change_payment_page) {
const subscriptionId = ppcp_add_payment_method.subscription_id_to_change_payment;
if(subscriptionId && result.data) {
const req = await fetch(ppcp_add_payment_method.ajax.subscription_change_payment_method.endpoint, {
method: "POST",
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nonce: ppcp_add_payment_method.ajax.subscription_change_payment_method.nonce,
subscription_id: subscriptionId,
payment_method: getCurrentPaymentMethod(),
wc_payment_token_id: result.data
})
});
const res = await req.json();
if (res.success === true) {
window.location.href = `${ppcp_add_payment_method.view_subscriptions_page}/${subscriptionId}`;
return;
}
}
return;
}
window.location.href = ppcp_add_payment_method.payment_methods_page;
return;
}
@ -167,7 +205,15 @@ document.addEventListener(
}
}
document.querySelector('#place_order').addEventListener("click", (event) => {
document.querySelector('#place_order')?.addEventListener("click", (event) => {
const cardPaymentToken = document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked')?.value;
if (
getCurrentPaymentMethod() !== 'ppcp-credit-card-gateway'
|| cardPaymentToken && cardPaymentToken !== 'new'
) {
return;
}
event.preventDefault();
cardField.submit()

View file

@ -9,7 +9,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CaptureCardPayment;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Helper\SavePaymentMethodsApplies;
@ -33,6 +32,750 @@ return array(
return apply_filters(
'woocommerce_paypal_payments_save_payment_methods_supported_country_currency_matrix',
array(
'AU' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'AT' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'BE' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'BG' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'CA' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'CY' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'CZ' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'DK' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'EE' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'FI' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'FR' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'DE' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'GR' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'HU' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'IE' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'IT' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'LV' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'LI' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'LT' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'LU' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'MT' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'NO' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'NL' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'PL' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'PT' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'RO' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'SK' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'SI' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'ES' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'SE' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'GB' => array(
'AUD',
'BRL',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'ILS',
'JPY',
'MXN',
'NOK',
'NZD',
'PHP',
'PLN',
'SEK',
'SGD',
'THB',
'TWD',
'USD',
),
'US' => array(
'AUD',
'CAD',
@ -61,30 +804,11 @@ return array(
$container->get( 'api.endpoint.payment-method-tokens' )
);
},
'save-payment-methods.wc-payment-tokens' => static function( ContainerInterface $container ): WooCommercePaymentTokens {
return new WooCommercePaymentTokens(
$container->get( 'vaulting.payment-token-helper' ),
$container->get( 'vaulting.payment-token-factory' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'save-payment-methods.endpoint.create-payment-token' => static function ( ContainerInterface $container ): CreatePaymentToken {
return new CreatePaymentToken(
$container->get( 'button.request-data' ),
$container->get( 'api.endpoint.payment-method-tokens' ),
$container->get( 'save-payment-methods.wc-payment-tokens' )
);
},
'save-payment-methods.endpoint.capture-card-payment' => static function( ContainerInterface $container ): CaptureCardPayment {
return new CaptureCardPayment(
$container->get( 'button.request-data' ),
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$container->get( 'api.factory.order' ),
$container->get( 'api.factory.purchase-unit' ),
$container->get( 'api.endpoint.order' ),
$container->get( 'session.handler' ),
$container->get( 'woocommerce.logger.woocommerce' )
$container->get( 'vaulting.wc-payment-tokens' )
);
},
);

View file

@ -1,219 +0,0 @@
<?php
/**
* The Capture Card Payment endpoint.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint;
use Psr\Log\LoggerInterface;
use RuntimeException;
use stdClass;
use WC_Payment_Tokens;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WP_Error;
/**
* Class CaptureCardPayment
*/
class CaptureCardPayment implements EndpointInterface {
use RequestTrait;
const ENDPOINT = 'ppc-capture-card-payment';
/**
* The request data.
*
* @var RequestData
*/
private $request_data;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The order factory.
*
* @var OrderFactory
*/
private $order_factory;
/**
* The purchase unit factory.
*
* @var PurchaseUnitFactory
*/
private $purchase_unit_factory;
/**
* The order endpoint.
*
* @var OrderEndpoint
*/
private $order_endpoint;
/**
* The session handler.
*
* @var SessionHandler
*/
private $session_handler;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* CaptureCardPayment constructor.
*
* @param RequestData $request_data The request data.
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param OrderFactory $order_factory The order factory.
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
* @param OrderEndpoint $order_endpoint The order endpoint.
* @param SessionHandler $session_handler The session handler.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
RequestData $request_data,
string $host,
Bearer $bearer,
OrderFactory $order_factory,
PurchaseUnitFactory $purchase_unit_factory,
OrderEndpoint $order_endpoint,
SessionHandler $session_handler,
LoggerInterface $logger
) {
$this->request_data = $request_data;
$this->host = $host;
$this->bearer = $bearer;
$this->order_factory = $order_factory;
$this->purchase_unit_factory = $purchase_unit_factory;
$this->order_endpoint = $order_endpoint;
$this->logger = $logger;
$this->session_handler = $session_handler;
}
/**
* Returns the nonce.
*
* @return string
*/
public static function nonce(): string {
return self::ENDPOINT;
}
/**
* Handles the request.
*
* @return bool
*/
public function handle_request(): bool {
$data = $this->request_data->read_request( $this->nonce() );
$tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id() );
foreach ( $tokens as $token ) {
if ( $token->get_id() === (int) $data['payment_token'] ) {
try {
$order = $this->create_order( $token->get_token() );
$id = $order->id ?? '';
$status = $order->status ?? '';
$payment_source = isset( $order->payment_source->card ) ? 'card' : '';
if ( $id && $status && $payment_source ) {
WC()->session->set(
'ppcp_saved_payment_card',
array(
'order_id' => $id,
'status' => $status,
'payment_source' => $payment_source,
)
);
wp_send_json_success();
return true;
}
} catch ( RuntimeException $exception ) {
wp_send_json_error();
return false;
}
}
}
wp_send_json_error();
return false;
}
/**
* Creates PayPal order from the given card vault id.
*
* @param string $vault_id Vault id.
* @return stdClass
* @throws RuntimeException When request fails.
*/
private function create_order( string $vault_id ): stdClass {
$items = array( $this->purchase_unit_factory->from_wc_cart() );
$data = array(
'intent' => 'CAPTURE',
'purchase_units' => array_map(
static function ( PurchaseUnit $item ): array {
return $item->to_array( true, false );
},
$items
),
'payment_source' => array(
'card' => array(
'vault_id' => $vault_id,
),
),
);
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
),
'body' => wp_json_encode( $data ),
);
$response = $this->request( $url, $args );
if ( $response instanceof WP_Error ) {
throw new RuntimeException( $response->get_error_message() );
}
return json_decode( $response['body'] );
}
}

View file

@ -14,9 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
use WooCommerce\PayPalCommerce\SavePaymentMethods\WooCommercePaymentTokens;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
/**
* Class CreatePaymentToken
@ -80,7 +78,8 @@ class CreatePaymentToken implements EndpointInterface {
*/
public function handle_request(): bool {
try {
$data = $this->request_data->read_request( $this->nonce() );
$data = $this->request_data->read_request( $this->nonce() );
$wc_token_id = 0;
/**
* Suppress ArgumentTypeCoercion
@ -98,7 +97,8 @@ class CreatePaymentToken implements EndpointInterface {
$result = $this->payment_method_tokens_endpoint->payment_tokens( $payment_source );
if ( is_user_logged_in() && isset( $result->customer->id ) ) {
update_user_meta( get_current_user_id(), '_ppcp_target_customer_id', $result->customer->id );
$current_user_id = get_current_user_id();
update_user_meta( $current_user_id, '_ppcp_target_customer_id', $result->customer->id );
if ( isset( $result->payment_source->paypal ) ) {
$email = '';
@ -106,34 +106,19 @@ class CreatePaymentToken implements EndpointInterface {
$email = $result->payment_source->paypal->email_address;
}
$this->wc_payment_tokens->create_payment_token_paypal(
get_current_user_id(),
$wc_token_id = $this->wc_payment_tokens->create_payment_token_paypal(
$current_user_id,
$result->id,
$email
);
}
if ( isset( $result->payment_source->card ) ) {
$token = new \WC_Payment_Token_CC();
$token->set_token( $result->id );
$token->set_user_id( get_current_user_id() );
$token->set_gateway_id( CreditCardGateway::ID );
$token->set_last4( $result->payment_source->card->last_digits ?? '' );
$expiry = explode( '-', $result->payment_source->card->expiry ?? '' );
$token->set_expiry_year( $expiry[0] ?? '' );
$token->set_expiry_month( $expiry[1] ?? '' );
$brand = $result->payment_source->card->brand ?? __( 'N/A', 'woocommerce-paypal-payments' );
if ( $brand ) {
$token->set_card_type( $brand );
}
$token->save();
$wc_token_id = $this->wc_payment_tokens->create_payment_token_card( $current_user_id, $result );
}
}
wp_send_json_success( $result );
wp_send_json_success( $wc_token_id );
return true;
} catch ( Exception $exception ) {
wp_send_json_error();

View file

@ -11,25 +11,26 @@ namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
use Psr\Log\LoggerInterface;
use WC_Order;
use WC_Payment_Tokens;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CaptureCardPayment;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod;
/**
* Class SavePaymentMethodsModule
@ -60,6 +61,30 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
return true;
}
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' );
assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint );
add_action(
'woocommerce_paypal_payments_gateway_migrate_on_update',
function() use ( $settings, $billing_agreements_endpoint ) {
$reference_transaction_enabled = $billing_agreements_endpoint->reference_transaction_enabled();
if ( $reference_transaction_enabled !== true ) {
$settings->set( 'vault_enabled', false );
$settings->persist();
}
}
);
if (
( ! $settings->has( 'vault_enabled' ) || ! $settings->get( 'vault_enabled' ) )
&& ( ! $settings->has( 'vault_enabled_dcc' ) || ! $settings->get( 'vault_enabled_dcc' ) )
) {
return true;
}
add_filter(
'woocommerce_paypal_payments_localized_script_data',
function( array $localized_script_data ) use ( $c ) {
@ -69,43 +94,19 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
$logger = $c->get( 'woocommerce.logger.woocommerce' );
assert( $logger instanceof LoggerInterface );
$localized_script_data = $this->add_id_token_to_script_data( $api, $logger, $localized_script_data );
$localized_script_data['ajax']['capture_card_payment'] = array(
'endpoint' => \WC_AJAX::get_endpoint( CaptureCardPayment::ENDPOINT ),
'nonce' => wp_create_nonce( CaptureCardPayment::nonce() ),
);
return $localized_script_data;
return $this->add_id_token_to_script_data( $api, $logger, $localized_script_data );
}
);
// Adds attributes needed to save payment method.
add_filter(
'ppcp_create_order_request_body_data',
function( array $data, string $payment_method, array $request_data ): array {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$wc_order_action = wc_clean( wp_unslash( $_POST['wc_order_action'] ?? '' ) );
if ( $wc_order_action === 'wcs_process_renewal' ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$subscription_id = wc_clean( wp_unslash( $_POST['post_ID'] ?? '' ) );
$subscription = wcs_get_subscription( (int) $subscription_id );
if ( $subscription ) {
$customer_id = $subscription->get_customer_id();
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
foreach ( $wc_tokens as $token ) {
$data['payment_source'] = array(
'paypal' => array(
'vault_id' => $token->get_token(),
),
);
return $data;
}
}
}
function( array $data, string $payment_method, array $request_data ) use ( $settings ): array {
if ( $payment_method === CreditCardGateway::ID ) {
if ( ! $settings->has( 'vault_enabled_dcc' ) || ! $settings->get( 'vault_enabled_dcc' ) ) {
return $data;
}
$save_payment_method = $request_data['save_payment_method'] ?? false;
if ( $save_payment_method ) {
$data['payment_source'] = array(
@ -119,6 +120,10 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
);
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
if ( ! $target_customer_id ) {
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
}
if ( $target_customer_id ) {
$data['payment_source']['card']['attributes']['customer'] = array(
'id' => $target_customer_id,
@ -128,16 +133,51 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
}
if ( $payment_method === PayPalGateway::ID ) {
$data['payment_source'] = array(
'paypal' => array(
'attributes' => array(
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
'usage_type' => 'MERCHANT',
if ( ! $settings->has( 'vault_enabled' ) || ! $settings->get( 'vault_enabled' ) ) {
return $data;
}
$funding_source = $request_data['funding_source'] ?? null;
if ( $funding_source && $funding_source === 'venmo' ) {
$data['payment_source'] = array(
'venmo' => array(
'attributes' => array(
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
'usage_type' => 'MERCHANT',
'permit_multiple_payment_tokens' => true,
),
),
),
),
);
);
} elseif ( $funding_source && $funding_source === 'apple_pay' ) {
$data['payment_source'] = array(
'apple_pay' => array(
'stored_credential' => array(
'payment_initiator' => 'CUSTOMER',
'payment_type' => 'RECURRING',
),
'attributes' => array(
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
),
),
),
);
} else {
$data['payment_source'] = array(
'paypal' => array(
'attributes' => array(
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
'usage_type' => 'MERCHANT',
'permit_multiple_payment_tokens' => true,
),
),
),
);
}
}
return $data;
@ -162,7 +202,7 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id );
$wc_payment_tokens = $c->get( 'save-payment-methods.wc-payment-tokens' );
$wc_payment_tokens = $c->get( 'vaulting.wc-payment-tokens' );
assert( $wc_payment_tokens instanceof WooCommercePaymentTokens );
if ( $wc_order->get_payment_method() === CreditCardGateway::ID ) {
@ -181,11 +221,29 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
}
if ( $wc_order->get_payment_method() === PayPalGateway::ID ) {
$wc_payment_tokens->create_payment_token_paypal(
$wc_order->get_customer_id(),
$token_id,
$payment_source->properties()->email_address ?? ''
);
switch ( $payment_source->name() ) {
case 'venmo':
$wc_payment_tokens->create_payment_token_venmo(
$wc_order->get_customer_id(),
$token_id,
$payment_source->properties()->email_address ?? ''
);
break;
case 'apple_pay':
$wc_payment_tokens->create_payment_token_applepay(
$wc_order->get_customer_id(),
$token_id
);
break;
case 'paypal':
default:
$wc_payment_tokens->create_payment_token_paypal(
$wc_order->get_customer_id(),
$token_id,
$payment_source->properties()->email_address ?? ''
);
break;
}
}
}
},
@ -194,13 +252,12 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
);
add_filter( 'woocommerce_paypal_payments_disable_add_payment_method', '__return_false' );
add_filter( 'woocommerce_paypal_payments_subscription_renewal_return_before_create_order_without_token', '__return_false' );
add_filter( 'woocommerce_paypal_payments_should_render_card_custom_fields', '__return_false' );
add_action(
'wp_enqueue_scripts',
function() use ( $c ) {
if ( ! is_user_logged_in() || ! $this->is_add_payment_method_page() ) {
if ( ! is_user_logged_in() || ! ( $this->is_add_payment_method_page() || $this->is_subscription_change_payment_method_page() ) ) {
return;
}
@ -220,6 +277,9 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
$target_customer_id = '';
if ( is_user_logged_in() ) {
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
if ( ! $target_customer_id ) {
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
}
}
$id_token = $api->id_token( $target_customer_id );
@ -228,17 +288,22 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
assert( $settings instanceof Settings );
$verification_method = $settings->has( '3d_secure_contingency' ) ? $settings->get( '3d_secure_contingency' ) : '';
$change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification
wp_localize_script(
'ppcp-add-payment-method',
'ppcp_add_payment_method',
array(
'client_id' => $c->get( 'button.client_id' ),
'merchant_id' => $c->get( 'api.merchant_id' ),
'id_token' => $id_token,
'payment_methods_page' => wc_get_account_endpoint_url( 'payment-methods' ),
'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ),
'verification_method' => $verification_method,
'ajax' => array(
'client_id' => $c->get( 'button.client_id' ),
'merchant_id' => $c->get( 'api.merchant_id' ),
'id_token' => $id_token,
'payment_methods_page' => wc_get_account_endpoint_url( 'payment-methods' ),
'view_subscriptions_page' => wc_get_account_endpoint_url( 'view-subscription' ),
'is_subscription_change_payment_page' => $this->is_subscription_change_payment_method_page(),
'subscription_id_to_change_payment' => $this->is_subscription_change_payment_method_page() ? (int) $change_payment_method : 0,
'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ),
'verification_method' => $verification_method,
'ajax' => array(
'create_setup_token' => array(
'endpoint' => \WC_AJAX::get_endpoint( CreateSetupToken::ENDPOINT ),
'nonce' => wp_create_nonce( CreateSetupToken::nonce() ),
@ -247,6 +312,10 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ),
'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ),
),
'subscription_change_payment_method' => array(
'endpoint' => \WC_AJAX::get_endpoint( SubscriptionChangePaymentMethod::ENDPOINT ),
'nonce' => wp_create_nonce( SubscriptionChangePaymentMethod::nonce() ),
),
),
)
);
@ -331,13 +400,10 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
}
);
add_action(
'wc_ajax_' . CaptureCardPayment::ENDPOINT,
static function () use ( $c ) {
$endpoint = $c->get( 'save-payment-methods.endpoint.capture-card-payment' );
assert( $endpoint instanceof CaptureCardPayment );
$endpoint->handle_request();
add_filter(
'woocommerce_paypal_payments_save_payment_methods_eligible',
function() {
return true;
}
);
@ -361,6 +427,9 @@ class SavePaymentMethodsModule implements ServiceModule, ExtendingModule, Execut
$target_customer_id = '';
if ( is_user_logged_in() ) {
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
if ( ! $target_customer_id ) {
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
}
}
$id_token = $api->id_token( $target_customer_id );

View file

@ -1,102 +0,0 @@
<?php
/**
* Service to create WC Payment Tokens.
*
* @package WooCommerce\PayPalCommerce\Applepay
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
use Exception;
use Psr\Log\LoggerInterface;
use WC_Payment_Tokens;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenHelper;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
/**
* Class WooCommercePaymentTokens
*/
class WooCommercePaymentTokens {
/**
* The payment token helper.
*
* @var PaymentTokenHelper
*/
private $payment_token_helper;
/**
* The payment token factory.
*
* @var PaymentTokenFactory
*/
private $payment_token_factory;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* WooCommercePaymentTokens constructor.
*
* @param PaymentTokenHelper $payment_token_helper The payment token helper.
* @param PaymentTokenFactory $payment_token_factory The payment token factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
PaymentTokenHelper $payment_token_helper,
PaymentTokenFactory $payment_token_factory,
LoggerInterface $logger
) {
$this->payment_token_helper = $payment_token_helper;
$this->payment_token_factory = $payment_token_factory;
$this->logger = $logger;
}
/**
* Creates a WC Payment Token for PayPal payment.
*
* @param int $customer_id The WC customer ID.
* @param string $token The PayPal payment token.
* @param string $email The PayPal customer email.
*
* @return void
*/
public function create_payment_token_paypal(
int $customer_id,
string $token,
string $email
): void {
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
if ( $this->payment_token_helper->token_exist( $wc_tokens, $token ) ) {
return;
}
$payment_token_paypal = $this->payment_token_factory->create( 'paypal' );
assert( $payment_token_paypal instanceof PaymentTokenPayPal );
$payment_token_paypal->set_token( $token );
$payment_token_paypal->set_user_id( $customer_id );
$payment_token_paypal->set_gateway_id( PayPalGateway::ID );
if ( $email && is_email( $email ) ) {
$payment_token_paypal->set_email( $email );
}
try {
$payment_token_paypal->save();
} catch ( Exception $exception ) {
$this->logger->error(
"Could not create WC payment token PayPal for customer {$customer_id}. " . $exception->getMessage()
);
}
}
}