mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 12:25:15 +08:00
Merge branch 'trunk' into PCP-417-new-feature---pay-upon-invoice
This commit is contained in:
commit
ade0e51e47
12 changed files with 229 additions and 66 deletions
|
@ -175,6 +175,15 @@ class PurchaseUnit {
|
|||
return $this->shipping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets shipping info.
|
||||
*
|
||||
* @param Shipping|null $shipping The value to set.
|
||||
*/
|
||||
public function set_shipping( ?Shipping $shipping ): void {
|
||||
$this->shipping = $shipping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reference id.
|
||||
*
|
||||
|
|
|
@ -119,4 +119,13 @@ class PayPalApiException extends RuntimeException {
|
|||
public function issues(): array {
|
||||
return $this->response->issues ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTTP status code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function status_code(): int {
|
||||
return $this->status_code;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,27 @@ class AmountFactory {
|
|||
$currency = $order->get_currency();
|
||||
$items = $this->item_factory->from_wc_order( $order );
|
||||
|
||||
$discount_value = array_sum(
|
||||
array(
|
||||
(float) $order->get_total_discount( false ), // Only coupons.
|
||||
$this->discounts_from_items( $items ),
|
||||
)
|
||||
);
|
||||
$discount = null;
|
||||
if ( $discount_value ) {
|
||||
$discount = new Money(
|
||||
(float) $discount_value,
|
||||
$currency
|
||||
);
|
||||
}
|
||||
|
||||
$items = array_filter(
|
||||
$items,
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() > 0;
|
||||
}
|
||||
);
|
||||
|
||||
$total_value = (float) $order->get_total();
|
||||
if ( (
|
||||
CreditCardGateway::ID === $order->get_payment_method()
|
||||
|
@ -160,14 +181,6 @@ class AmountFactory {
|
|||
$currency
|
||||
);
|
||||
|
||||
$discount = null;
|
||||
if ( (float) $order->get_total_discount( false ) ) {
|
||||
$discount = new Money(
|
||||
(float) $order->get_total_discount( false ),
|
||||
$currency
|
||||
);
|
||||
}
|
||||
|
||||
$breakdown = new AmountBreakdown(
|
||||
$item_total,
|
||||
$shipping,
|
||||
|
@ -251,4 +264,29 @@ class AmountFactory {
|
|||
|
||||
return new AmountBreakdown( ...$money );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of items with negative amount;
|
||||
*
|
||||
* @param Item[] $items PayPal order items.
|
||||
* @return float
|
||||
*/
|
||||
private function discounts_from_items( array $items ): float {
|
||||
$discounts = array_filter(
|
||||
$items,
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() < 0;
|
||||
}
|
||||
);
|
||||
return abs(
|
||||
array_sum(
|
||||
array_map(
|
||||
function ( Item $item ): float {
|
||||
return (float) $item->quantity() * $item->unit_amount()->value();
|
||||
},
|
||||
$discounts
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ class ItemFactory {
|
|||
mb_substr( $product->get_name(), 0, 127 ),
|
||||
new Money( $price_without_tax_rounded, $currency ),
|
||||
$quantity,
|
||||
mb_substr( wp_strip_all_tags( $product->get_description() ), 0, 127 ),
|
||||
substr( wp_strip_all_tags( $product->get_description() ), 0, 127 ) ?: '',
|
||||
$tax,
|
||||
$product->get_sku(),
|
||||
( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS,
|
||||
|
|
|
@ -107,7 +107,12 @@ class PurchaseUnitFactory {
|
|||
*/
|
||||
public function from_wc_order( \WC_Order $order ): PurchaseUnit {
|
||||
$amount = $this->amount_factory->from_wc_order( $order );
|
||||
$items = $this->item_factory->from_wc_order( $order );
|
||||
$items = array_filter(
|
||||
$this->item_factory->from_wc_order( $order ),
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() > 0;
|
||||
}
|
||||
);
|
||||
$shipping = $this->shipping_factory->from_wc_order( $order );
|
||||
if (
|
||||
! $this->shipping_needed( ... array_values( $items ) ) ||
|
||||
|
@ -153,7 +158,12 @@ class PurchaseUnitFactory {
|
|||
*/
|
||||
public function from_wc_cart( \WC_Cart $cart ): PurchaseUnit {
|
||||
$amount = $this->amount_factory->from_wc_cart( $cart );
|
||||
$items = $this->item_factory->from_wc_cart( $cart );
|
||||
$items = array_filter(
|
||||
$this->item_factory->from_wc_cart( $cart ),
|
||||
function ( Item $item ): bool {
|
||||
return $item->unit_amount()->value() > 0;
|
||||
}
|
||||
);
|
||||
|
||||
$shipping = null;
|
||||
$customer = \WC()->customer;
|
||||
|
|
|
@ -209,12 +209,6 @@ class CreditCardRenderer {
|
|||
const firstName = document.getElementById('billing_first_name') ? document.getElementById('billing_first_name').value : '';
|
||||
const lastName = document.getElementById('billing_last_name') ? document.getElementById('billing_last_name').value : '';
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
this.spinner.unblock();
|
||||
this.errorHandler.message(this.defaultConfig.hosted_fields.labels.cardholder_name_required);
|
||||
return;
|
||||
}
|
||||
|
||||
hostedFieldsData.cardholderName = firstName + ' ' + lastName;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
|||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
|
@ -242,6 +243,13 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
}
|
||||
|
||||
$order = $this->create_paypal_order( $wc_order );
|
||||
|
||||
if ( 'pay-now' === $data['context'] && is_a( $wc_order, \WC_Order::class ) ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
|
||||
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
|
||||
$wc_order->save_meta_data();
|
||||
}
|
||||
|
||||
wp_send_json_success( $order->to_array() );
|
||||
return true;
|
||||
|
||||
|
@ -317,19 +325,51 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* @return Order Created PayPal order.
|
||||
*
|
||||
* @throws RuntimeException If create order request fails.
|
||||
* @throws PayPalApiException If create order request fails.
|
||||
* phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
private function create_paypal_order( \WC_Order $wc_order = null ): Order {
|
||||
$needs_shipping = WC()->cart instanceof \WC_Cart && WC()->cart->needs_shipping();
|
||||
$shipping_address_is_fix = $needs_shipping && 'checkout' === $this->parsed_request_data['context'];
|
||||
|
||||
return $this->api_endpoint->create(
|
||||
$this->purchase_units,
|
||||
$this->payer( $this->parsed_request_data, $wc_order ),
|
||||
null,
|
||||
$this->payment_method(),
|
||||
'',
|
||||
$shipping_address_is_fix
|
||||
);
|
||||
try {
|
||||
return $this->api_endpoint->create(
|
||||
$this->purchase_units,
|
||||
$this->payer( $this->parsed_request_data, $wc_order ),
|
||||
null,
|
||||
$this->payment_method(),
|
||||
'',
|
||||
$shipping_address_is_fix
|
||||
);
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
// Looks like currently there is no proper way to validate the shipping address for PayPal,
|
||||
// so we cannot make some invalid addresses null in PurchaseUnitFactory,
|
||||
// which causes failure e.g. for guests using the button on products pages when the country does not have postal codes.
|
||||
if ( 422 === $exception->status_code()
|
||||
&& array_filter(
|
||||
$exception->details(),
|
||||
function ( stdClass $detail ): bool {
|
||||
return isset( $detail->field ) && str_contains( (string) $detail->field, 'shipping/address' );
|
||||
}
|
||||
) ) {
|
||||
$this->logger->info( 'Invalid shipping address for order creation, retrying without it.' );
|
||||
|
||||
foreach ( $this->purchase_units as $purchase_unit ) {
|
||||
$purchase_unit->set_shipping( null );
|
||||
}
|
||||
|
||||
return $this->api_endpoint->create(
|
||||
$this->purchase_units,
|
||||
$this->payer( $this->parsed_request_data, $wc_order ),
|
||||
null,
|
||||
$this->payment_method(),
|
||||
'',
|
||||
$shipping_address_is_fix
|
||||
);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -150,7 +150,8 @@ class OrderProcessor {
|
|||
* @return bool
|
||||
*/
|
||||
public function process( \WC_Order $wc_order ): bool {
|
||||
$order = $this->session_handler->order();
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
$order = $this->session_handler->order() ?? $this->order_endpoint->order( $order_id );
|
||||
if ( ! $order ) {
|
||||
$this->last_error = __( 'No PayPal order found in the current WooCommerce session.', 'woocommerce-paypal-payments' );
|
||||
return false;
|
||||
|
|
|
@ -167,7 +167,7 @@ class SettingsListener {
|
|||
* Prevent enabling both Pay Later messaging and PayPal vaulting
|
||||
*/
|
||||
public function listen_for_vaulting_enabled() {
|
||||
if ( ! $this->is_valid_site_request() ) {
|
||||
if ( ! $this->is_valid_site_request() || State::STATE_ONBOARDED !== $this->state->current_state() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -208,10 +208,6 @@ class SettingsListener {
|
|||
$this->settings->set( 'message_product_enabled', false );
|
||||
$this->settings->set( 'message_cart_enabled', false );
|
||||
$this->settings->persist();
|
||||
|
||||
$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' );
|
||||
wp_safe_redirect( $redirect_url, 302 );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -442,19 +438,10 @@ class SettingsListener {
|
|||
*/
|
||||
private function is_valid_site_request() : bool {
|
||||
|
||||
/**
|
||||
* No nonce needed at this point.
|
||||
*
|
||||
* phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
* phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
*/
|
||||
if ( empty( $this->page_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue