mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 13:44:42 +08:00
Merge remote-tracking branch 'origin/trunk' into PCP-677-smart-button-not-visible-on-single-product-when-subscription-in-cart
This commit is contained in:
commit
31b5ca90e8
78 changed files with 3536 additions and 522 deletions
|
@ -2,3 +2,6 @@
|
|||
if (!defined('EP_PAGES')) {
|
||||
define('EP_PAGES', 4096);
|
||||
}
|
||||
if (!defined('MONTH_IN_SECONDS')) {
|
||||
define('MONTH_IN_SECONDS', 30 * DAY_IN_SECONDS);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 1.8.2 - TBD =
|
||||
= 1.9.1 - TBD =
|
||||
* Fix - ITEM_TOTAL_MISMATCH error when checking out with multiple products #721
|
||||
* Fix - Unable to purchase a product with Credit card button in pay for order page #718
|
||||
* Fix - Pay Later messaging only displayed when smart button is active on the same page #283
|
||||
* Fix - Pay Later messaging displayed for out of stock variable products or with no variation selected #667
|
||||
* Fix - Placeholders and card type detection not working for PayPal Card Processing (260) #685
|
||||
* Fix - PUI gateway is displayed with unsupported store currency #711
|
||||
* Enhancement - Missing PayPal fee in WC order details for PUI purchase #714
|
||||
* Enhancement - Skip loading of PUI js file on all pages where PUI gateway is not displayed #723
|
||||
* Enhancement - PUI feature capitalization not consistent #724
|
||||
|
||||
= 1.9.0 - 2022-07-04 =
|
||||
* Add - New Feature - Pay Upon Invoice (Germany only) #608
|
||||
* Fix - Order not approved: payment via vaulted PayPal account fails #677
|
||||
* Fix - Cant' refund : "ERROR Refund failed: No country given for address." #639
|
||||
* Fix - Something went wrong error in Virtual products when using vaulted payment #673
|
||||
|
|
|
@ -43,13 +43,13 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerReceivableBreakdownFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerStatusFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
|
@ -221,10 +221,6 @@ return array(
|
|||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
return new PartnerReferralsData( $dcc_applies );
|
||||
},
|
||||
'api.repository.cart' => static function ( ContainerInterface $container ): CartRepository {
|
||||
$factory = $container->get( 'api.factory.purchase-unit' );
|
||||
return new CartRepository( $factory );
|
||||
},
|
||||
'api.repository.payee' => static function ( ContainerInterface $container ): PayeeRepository {
|
||||
$merchant_email = $container->get( 'api.merchant_email' );
|
||||
$merchant_id = $container->get( 'api.merchant_id' );
|
||||
|
@ -298,6 +294,9 @@ return array(
|
|||
$address_factory = $container->get( 'api.factory.address' );
|
||||
return new ShippingFactory( $address_factory );
|
||||
},
|
||||
'api.factory.shipping-preference' => static function ( ContainerInterface $container ): ShippingPreferenceFactory {
|
||||
return new ShippingPreferenceFactory();
|
||||
},
|
||||
'api.factory.amount' => static function ( ContainerInterface $container ): AmountFactory {
|
||||
$item_factory = $container->get( 'api.factory.item' );
|
||||
return new AmountFactory(
|
||||
|
|
|
@ -163,57 +163,23 @@ class OrderEndpoint {
|
|||
* Creates an order.
|
||||
*
|
||||
* @param PurchaseUnit[] $items The purchase unit items for the order.
|
||||
* @param string $shipping_preference One of ApplicationContext::SHIPPING_PREFERENCE_ values.
|
||||
* @param Payer|null $payer The payer off the order.
|
||||
* @param PaymentToken|null $payment_token The payment token.
|
||||
* @param PaymentMethod|null $payment_method The payment method.
|
||||
* @param string $paypal_request_id The paypal request id.
|
||||
* @param bool $shipping_address_is_fixed Whether the shipping address is changeable or not.
|
||||
*
|
||||
* @return Order
|
||||
* @throws RuntimeException If the request fails.
|
||||
*/
|
||||
public function create(
|
||||
array $items,
|
||||
string $shipping_preference,
|
||||
Payer $payer = null,
|
||||
PaymentToken $payment_token = null,
|
||||
PaymentMethod $payment_method = null,
|
||||
string $paypal_request_id = '',
|
||||
bool $shipping_address_is_fixed = false
|
||||
string $paypal_request_id = ''
|
||||
): Order {
|
||||
|
||||
$contains_physical_goods = false;
|
||||
$items = array_filter(
|
||||
$items,
|
||||
static function ( $item ) use ( &$contains_physical_goods ): bool {
|
||||
$is_purchase_unit = is_a( $item, PurchaseUnit::class );
|
||||
/**
|
||||
* A purchase unit.
|
||||
*
|
||||
* @var PurchaseUnit $item
|
||||
*/
|
||||
if ( $is_purchase_unit && $item->contains_physical_goods() ) {
|
||||
$contains_physical_goods = true;
|
||||
}
|
||||
|
||||
return $is_purchase_unit;
|
||||
}
|
||||
);
|
||||
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
if ( $contains_physical_goods ) {
|
||||
if ( $shipping_address_is_fixed ) {
|
||||
// Checkout + no address given? Probably something weird happened, like no form validation?
|
||||
// Also note that $items currently always seems to be an array with one item.
|
||||
if ( $this->has_items_without_shipping( $items ) ) {
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
} else {
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
|
||||
}
|
||||
} else {
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$data = array(
|
||||
'intent' => ( $this->subscription_helper->cart_contains_subscription() || $this->subscription_helper->current_product_is_subscription() ) ? 'AUTHORIZE' : $this->intent,
|
||||
|
@ -598,20 +564,4 @@ class OrderEndpoint {
|
|||
$new_order = $this->order( $order_to_update->id() );
|
||||
return $new_order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is at least one item without shipping.
|
||||
*
|
||||
* @param PurchaseUnit[] $items The items.
|
||||
* @return bool Whether items contains shipping or not.
|
||||
*/
|
||||
private function has_items_without_shipping( array $items ): bool {
|
||||
foreach ( $items as $item ) {
|
||||
if ( ! $item->shipping() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
<?php
|
||||
/**
|
||||
* Create order for PUI.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSource;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class OrderEndpoint.
|
||||
*/
|
||||
class PayUponInvoiceOrderEndpoint {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
protected $bearer;
|
||||
|
||||
/**
|
||||
* The order factory.
|
||||
*
|
||||
* @var OrderFactory
|
||||
*/
|
||||
protected $order_factory;
|
||||
|
||||
/**
|
||||
* The FraudNet entity.
|
||||
*
|
||||
* @var FraudNet
|
||||
*/
|
||||
protected $fraudnet;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* OrderEndpoint constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param OrderFactory $order_factory The order factory.
|
||||
* @param FraudNet $fraudnet FrauNet entity.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
OrderFactory $order_factory,
|
||||
FraudNet $fraudnet,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->order_factory = $order_factory;
|
||||
$this->logger = $logger;
|
||||
$this->fraudnet = $fraudnet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an order.
|
||||
*
|
||||
* @param PurchaseUnit[] $items The purchase unit items for the order.
|
||||
* @param PaymentSource $payment_source The payment source.
|
||||
* @return Order
|
||||
* @throws RuntimeException When there is a problem with the payment source.
|
||||
* @throws PayPalApiException When there is a problem creating the order.
|
||||
*/
|
||||
public function create( array $items, PaymentSource $payment_source ): Order {
|
||||
|
||||
$data = array(
|
||||
'intent' => 'CAPTURE',
|
||||
'processing_instruction' => 'ORDER_COMPLETE_ON_PAYMENT_APPROVAL',
|
||||
'purchase_units' => array_map(
|
||||
static function ( PurchaseUnit $item ): array {
|
||||
return $item->to_array();
|
||||
},
|
||||
$items
|
||||
),
|
||||
'payment_source' => array(
|
||||
'pay_upon_invoice' => $payment_source->to_array(),
|
||||
),
|
||||
);
|
||||
|
||||
$data = $this->ensure_tax( $data );
|
||||
$data = $this->ensure_tax_rate( $data );
|
||||
$data = $this->ensure_shipping( $data, $payment_source->to_array() );
|
||||
|
||||
$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',
|
||||
'Prefer' => 'return=representation',
|
||||
'PayPal-Client-Metadata-Id' => $this->fraudnet->session_id(),
|
||||
'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() );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
|
||||
$issue = $json->details[0]->issue ?? null;
|
||||
|
||||
$site_country_code = explode( '-', get_bloginfo( 'language' ) )[0] ?? '';
|
||||
if ( 'PAYMENT_SOURCE_INFO_CANNOT_BE_VERIFIED' === $issue ) {
|
||||
if ( 'de' === $site_country_code ) {
|
||||
throw new RuntimeException( 'Die Kombination aus Ihrem Namen und Ihrer Anschrift konnte nicht validiert werden. Bitte korrigieren Sie Ihre Daten und versuchen Sie es erneut. Weitere Informationen finden Sie in den Ratepay <a href="https://www.ratepay.com/legal-payment-dataprivacy/?lang=de" target="_blank">Datenschutzbestimmungen</a> oder nutzen Sie das Ratepay <a href="https://www.ratepay.com/kontakt/" target="_blank">Kontaktformular</a>.' );
|
||||
} else {
|
||||
throw new RuntimeException( 'The combination of your name and address could not be validated. Please correct your data and try again. You can find further information in the <a href="https://www.ratepay.com/en/ratepay-data-privacy-statement/" target="_blank">Ratepay Data Privacy Statement</a> or you can contact Ratepay using this <a href="https://www.ratepay.com/en/contact/" target="_blank">contact form</a>.' );
|
||||
}
|
||||
}
|
||||
if ( 'PAYMENT_SOURCE_DECLINED_BY_PROCESSOR' === $issue ) {
|
||||
if ( 'de' === $site_country_code ) {
|
||||
throw new RuntimeException( 'Die gewählte Zahlungsart kann nicht genutzt werden. Diese Entscheidung basiert auf einem automatisierten <a href="https://www.ratepay.com/legal-payment-dataprivacy/?lang=de" target="_blank">Datenverarbeitungsverfahren</a>. Weitere Informationen finden Sie in den Ratepay Datenschutzbestimmungen oder nutzen Sie das Ratepay <a href="https://www.ratepay.com/kontakt/" target="_blank">Kontaktformular</a>.' );
|
||||
} else {
|
||||
throw new RuntimeException( 'It is not possible to use the selected payment method. This decision is based on automated data processing. You can find further information in the <a href="https://www.ratepay.com/en/ratepay-data-privacy-statement/" target="_blank">Ratepay Data Privacy Statement</a> or you can contact Ratepay using this <a href="https://www.ratepay.com/en/contact/" target="_blank">contact form</a>.' );
|
||||
}
|
||||
}
|
||||
|
||||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
|
||||
return $this->order_factory->from_paypal_response( $json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PayPal order as object.
|
||||
*
|
||||
* @param string $id The PayPal order ID.
|
||||
* @return stdClass
|
||||
* @throws RuntimeException When there is a problem getting the order.
|
||||
* @throws PayPalApiException When there is a problem getting the order.
|
||||
*/
|
||||
public function order( string $id ): stdClass {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $id;
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures items contains tax.
|
||||
*
|
||||
* @param array $data The data.
|
||||
* @return array
|
||||
*/
|
||||
private function ensure_tax( array $data ): array {
|
||||
$items_count = count( $data['purchase_units'][0]['items'] );
|
||||
|
||||
for ( $i = 0; $i < $items_count; $i++ ) {
|
||||
if ( ! isset( $data['purchase_units'][0]['items'][ $i ]['tax'] ) ) {
|
||||
$data['purchase_units'][0]['items'][ $i ]['tax'] = array(
|
||||
'currency_code' => 'EUR',
|
||||
'value' => '0.00',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures items contains tax rate.
|
||||
*
|
||||
* @param array $data The data.
|
||||
* @return array
|
||||
*/
|
||||
private function ensure_tax_rate( array $data ): array {
|
||||
$items_count = count( $data['purchase_units'][0]['items'] );
|
||||
|
||||
for ( $i = 0; $i < $items_count; $i++ ) {
|
||||
if ( ! isset( $data['purchase_units'][0]['items'][ $i ]['tax_rate'] ) ) {
|
||||
$data['purchase_units'][0]['items'][ $i ]['tax_rate'] = '0.00';
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures purchase units contains shipping by using payment source data.
|
||||
*
|
||||
* @param array $data The data.
|
||||
* @param array $payment_source The payment source.
|
||||
* @return array
|
||||
*/
|
||||
private function ensure_shipping( array $data, array $payment_source ): array {
|
||||
if ( isset( $data['purchase_units'][0]['shipping'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$given_name = $payment_source['name']['given_name'] ?? '';
|
||||
$surname = $payment_source['name']['surname'] ?? '';
|
||||
$address = $payment_source['billing_address'] ?? array();
|
||||
|
||||
$data['purchase_units'][0]['shipping']['name'] = array( 'full_name' => $given_name . ' ' . $surname );
|
||||
$data['purchase_units'][0]['shipping']['address'] = $address;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -87,9 +87,11 @@ trait RequestTrait {
|
|||
if ( isset( $response['response'] ) ) {
|
||||
$output .= 'Response: ' . wc_print_r( $response['response'], true ) . "\n";
|
||||
|
||||
if ( isset( $response['body'] )
|
||||
if (
|
||||
isset( $response['body'] )
|
||||
&& isset( $response['response']['code'] )
|
||||
&& ! in_array( $response['response']['code'], array( 200, 201, 202, 204 ), true ) ) {
|
||||
&& ! in_array( $response['response']['code'], array( 200, 201, 202, 204 ), true )
|
||||
) {
|
||||
$output .= 'Response Body: ' . wc_print_r( $response['body'], true ) . "\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,15 @@ class Amount {
|
|||
return $this->money->value();
|
||||
}
|
||||
|
||||
/**
|
||||
* The value formatted as string for API requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function value_str(): string {
|
||||
return $this->money->value_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the breakdown.
|
||||
*
|
||||
|
@ -79,12 +88,7 @@ class Amount {
|
|||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
$amount = array(
|
||||
'currency_code' => $this->currency_code(),
|
||||
'value' => in_array( $this->currency_code(), $this->currencies_without_decimals, true )
|
||||
? round( $this->value(), 0 )
|
||||
: number_format( $this->value(), 2, '.', '' ),
|
||||
);
|
||||
$amount = $this->money->to_array();
|
||||
if ( $this->breakdown() && count( $this->breakdown()->to_array() ) ) {
|
||||
$amount['breakdown'] = $this->breakdown()->to_array();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
|||
*/
|
||||
class Item {
|
||||
|
||||
|
||||
const PHYSICAL_GOODS = 'PHYSICAL_GOODS';
|
||||
const DIGITAL_GOODS = 'DIGITAL_GOODS';
|
||||
|
||||
|
@ -67,6 +66,13 @@ class Item {
|
|||
*/
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* The tax rate.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $tax_rate;
|
||||
|
||||
/**
|
||||
* Item constructor.
|
||||
*
|
||||
|
@ -77,6 +83,7 @@ class Item {
|
|||
* @param Money|null $tax The tax.
|
||||
* @param string $sku The SKU.
|
||||
* @param string $category The category.
|
||||
* @param float $tax_rate The tax rate.
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
|
@ -85,7 +92,8 @@ class Item {
|
|||
string $description = '',
|
||||
Money $tax = null,
|
||||
string $sku = '',
|
||||
string $category = 'PHYSICAL_GOODS'
|
||||
string $category = 'PHYSICAL_GOODS',
|
||||
float $tax_rate = 0
|
||||
) {
|
||||
|
||||
$this->name = $name;
|
||||
|
@ -94,8 +102,9 @@ class Item {
|
|||
$this->description = $description;
|
||||
$this->tax = $tax;
|
||||
$this->sku = $sku;
|
||||
$this->category = ( self::DIGITAL_GOODS === $category ) ?
|
||||
self::DIGITAL_GOODS : self::PHYSICAL_GOODS;
|
||||
$this->category = ( self::DIGITAL_GOODS === $category ) ? self::DIGITAL_GOODS : self::PHYSICAL_GOODS;
|
||||
$this->category = $category;
|
||||
$this->tax_rate = $tax_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,6 +170,15 @@ class Item {
|
|||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tax rate.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function tax_rate():float {
|
||||
return round( (float) $this->tax_rate, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
|
@ -180,6 +198,10 @@ class Item {
|
|||
$item['tax'] = $this->tax()->to_array();
|
||||
}
|
||||
|
||||
if ( $this->tax_rate() ) {
|
||||
$item['tax_rate'] = (string) $this->tax_rate();
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\MoneyFormatter;
|
||||
|
||||
/**
|
||||
* Class Money
|
||||
*/
|
||||
|
@ -29,11 +31,11 @@ class Money {
|
|||
private $value;
|
||||
|
||||
/**
|
||||
* Currencies that does not support decimals.
|
||||
* The MoneyFormatter.
|
||||
*
|
||||
* @var array
|
||||
* @var MoneyFormatter
|
||||
*/
|
||||
private $currencies_without_decimals = array( 'HUF', 'JPY', 'TWD' );
|
||||
private $money_formatter;
|
||||
|
||||
/**
|
||||
* Money constructor.
|
||||
|
@ -44,6 +46,8 @@ class Money {
|
|||
public function __construct( float $value, string $currency_code ) {
|
||||
$this->value = $value;
|
||||
$this->currency_code = $currency_code;
|
||||
|
||||
$this->money_formatter = new MoneyFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +59,15 @@ class Money {
|
|||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value formatted as string for API requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function value_str(): string {
|
||||
return $this->money_formatter->format( $this->value, $this->currency_code );
|
||||
}
|
||||
|
||||
/**
|
||||
* The currency code.
|
||||
*
|
||||
|
@ -72,9 +85,7 @@ class Money {
|
|||
public function to_array(): array {
|
||||
return array(
|
||||
'currency_code' => $this->currency_code(),
|
||||
'value' => in_array( $this->currency_code(), $this->currencies_without_decimals, true )
|
||||
? round( $this->value(), 0 )
|
||||
: number_format( $this->value(), 2, '.', '' ),
|
||||
'value' => $this->value_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,21 +15,21 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
|||
* Class OrderStatus
|
||||
*/
|
||||
class OrderStatus {
|
||||
|
||||
|
||||
const INTERNAL = 'INTERNAL';
|
||||
const CREATED = 'CREATED';
|
||||
const SAVED = 'SAVED';
|
||||
const APPROVED = 'APPROVED';
|
||||
const VOIDED = 'VOIDED';
|
||||
const COMPLETED = 'COMPLETED';
|
||||
const VALID_STATI = array(
|
||||
const PENDING_APPROVAL = 'PENDING_APPROVAL';
|
||||
const VALID_STATUS = array(
|
||||
self::INTERNAL,
|
||||
self::CREATED,
|
||||
self::SAVED,
|
||||
self::APPROVED,
|
||||
self::VOIDED,
|
||||
self::COMPLETED,
|
||||
self::PENDING_APPROVAL,
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,7 @@ class OrderStatus {
|
|||
* @throws RuntimeException When the status is not valid.
|
||||
*/
|
||||
public function __construct( string $status ) {
|
||||
if ( ! in_array( $status, self::VALID_STATI, true ) ) {
|
||||
if ( ! in_array( $status, self::VALID_STATUS, true ) ) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
// translators: %s is the current status.
|
||||
|
|
|
@ -331,9 +331,9 @@ class PurchaseUnit {
|
|||
$remaining_item_total = array_reduce(
|
||||
$items,
|
||||
function ( float $total, Item $item ): float {
|
||||
return $total - $item->unit_amount()->value() * (float) $item->quantity();
|
||||
return $total - (float) $item->unit_amount()->value_str() * (float) $item->quantity();
|
||||
},
|
||||
$item_total->value()
|
||||
(float) $item_total->value_str()
|
||||
);
|
||||
|
||||
$remaining_item_total = round( $remaining_item_total, 2 );
|
||||
|
@ -356,11 +356,11 @@ class PurchaseUnit {
|
|||
function ( float $total, Item $item ): float {
|
||||
$tax = $item->tax();
|
||||
if ( $tax ) {
|
||||
$total -= $tax->value() * (float) $item->quantity();
|
||||
$total -= (float) $tax->value_str() * (float) $item->quantity();
|
||||
}
|
||||
return $total;
|
||||
},
|
||||
$tax_total->value()
|
||||
(float) $tax_total->value_str()
|
||||
);
|
||||
|
||||
$remaining_tax_total = round( $remaining_tax_total, 2 );
|
||||
|
@ -378,29 +378,29 @@ class PurchaseUnit {
|
|||
|
||||
$amount_total = 0.0;
|
||||
if ( $shipping ) {
|
||||
$amount_total += $shipping->value();
|
||||
$amount_total += (float) $shipping->value_str();
|
||||
}
|
||||
if ( $item_total ) {
|
||||
$amount_total += $item_total->value();
|
||||
$amount_total += (float) $item_total->value_str();
|
||||
}
|
||||
if ( $discount ) {
|
||||
$amount_total -= $discount->value();
|
||||
$amount_total -= (float) $discount->value_str();
|
||||
}
|
||||
if ( $tax_total ) {
|
||||
$amount_total += $tax_total->value();
|
||||
$amount_total += (float) $tax_total->value_str();
|
||||
}
|
||||
if ( $shipping_discount ) {
|
||||
$amount_total -= $shipping_discount->value();
|
||||
$amount_total -= (float) $shipping_discount->value_str();
|
||||
}
|
||||
if ( $handling ) {
|
||||
$amount_total += $handling->value();
|
||||
$amount_total += (float) $handling->value_str();
|
||||
}
|
||||
if ( $insurance ) {
|
||||
$amount_total += $insurance->value();
|
||||
$amount_total += (float) $insurance->value_str();
|
||||
}
|
||||
|
||||
$amount_str = (string) $amount->to_array()['value'];
|
||||
$amount_total_str = (string) ( new Money( $amount_total, $amount->currency_code() ) )->to_array()['value'];
|
||||
$amount_str = $amount->value_str();
|
||||
$amount_total_str = ( new Money( $amount_total, $amount->currency_code() ) )->value_str();
|
||||
$needs_to_ditch = $amount_str !== $amount_total_str;
|
||||
return $needs_to_ditch;
|
||||
}
|
||||
|
|
|
@ -152,11 +152,15 @@ class PurchaseUnitFactory {
|
|||
/**
|
||||
* Creates a PurchaseUnit based off a WooCommerce cart.
|
||||
*
|
||||
* @param \WC_Cart $cart The cart.
|
||||
* @param \WC_Cart|null $cart The cart.
|
||||
*
|
||||
* @return PurchaseUnit
|
||||
*/
|
||||
public function from_wc_cart( \WC_Cart $cart ): PurchaseUnit {
|
||||
public function from_wc_cart( ?\WC_Cart $cart = null ): PurchaseUnit {
|
||||
if ( ! $cart ) {
|
||||
$cart = WC()->cart ?? new \WC_Cart();
|
||||
}
|
||||
|
||||
$amount = $this->amount_factory->from_wc_cart( $cart );
|
||||
$items = array_filter(
|
||||
$this->item_factory->from_wc_cart( $cart ),
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Returns shipping_preference for the given state.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use WC_Cart;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
|
||||
/**
|
||||
* Class ShippingPreferenceFactory
|
||||
*/
|
||||
class ShippingPreferenceFactory {
|
||||
|
||||
/**
|
||||
* Returns shipping_preference for the given state.
|
||||
*
|
||||
* @param PurchaseUnit $purchase_unit Thw PurchaseUnit.
|
||||
* @param string $context The operation context like 'checkout', 'cart'.
|
||||
* @param WC_Cart|null $cart The current cart if relevant.
|
||||
* @param string $funding_source The funding source (PayPal button) like 'paypal', 'venmo', 'card'.
|
||||
* @return string
|
||||
*/
|
||||
public function from_state(
|
||||
PurchaseUnit $purchase_unit,
|
||||
string $context,
|
||||
?WC_Cart $cart = null,
|
||||
string $funding_source = ''
|
||||
): string {
|
||||
$contains_physical_goods = $purchase_unit->contains_physical_goods();
|
||||
if ( ! $contains_physical_goods ) {
|
||||
return ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
}
|
||||
|
||||
$has_shipping = null !== $purchase_unit->shipping();
|
||||
$needs_shipping = $cart && $cart->needs_shipping();
|
||||
$shipping_address_is_fixed = $needs_shipping && 'checkout' === $context;
|
||||
|
||||
if ( $shipping_address_is_fixed ) {
|
||||
// Checkout + no address given? Probably something weird happened, like no form validation?
|
||||
if ( ! $has_shipping ) {
|
||||
return ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
}
|
||||
|
||||
return ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
|
||||
}
|
||||
|
||||
if ( 'card' === $funding_source ) {
|
||||
if ( ! $has_shipping ) {
|
||||
return ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
}
|
||||
// Looks like GET_FROM_FILE does not work for the vaulted card button.
|
||||
return ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
|
||||
}
|
||||
|
||||
return ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE;
|
||||
}
|
||||
}
|
|
@ -67,10 +67,11 @@ class Cache {
|
|||
*
|
||||
* @param string $key The key under which the value should be cached.
|
||||
* @param mixed $value The value to cache.
|
||||
* @param int $expiration Time until expiration in seconds.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set( string $key, $value ): bool {
|
||||
return (bool) set_transient( $this->prefix . $key, $value );
|
||||
public function set( string $key, $value, int $expiration = 0 ): bool {
|
||||
return (bool) set_transient( $this->prefix . $key, $value, $expiration );
|
||||
}
|
||||
}
|
||||
|
|
36
modules/ppcp-api-client/src/Helper/MoneyFormatter.php
Normal file
36
modules/ppcp-api-client/src/Helper/MoneyFormatter.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
/**
|
||||
* Class MoneyFormatter.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
|
||||
|
||||
/**
|
||||
* Class MoneyFormatter
|
||||
*/
|
||||
class MoneyFormatter {
|
||||
/**
|
||||
* Currencies that does not support decimals.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $currencies_without_decimals = array( 'HUF', 'JPY', 'TWD' );
|
||||
|
||||
/**
|
||||
* Returns the value formatted as string for API requests.
|
||||
*
|
||||
* @param float $value The value.
|
||||
* @param string $currency The 3-letter currency code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format( float $value, string $currency ): string {
|
||||
return in_array( $currency, $this->currencies_without_decimals, true )
|
||||
? (string) round( $value, 0 )
|
||||
: number_format( $value, 2, '.', '' );
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* The cart repository returns the purchase units from the current \WC_Cart.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
|
||||
/**
|
||||
* Class CartRepository
|
||||
*/
|
||||
class CartRepository implements PurchaseUnitRepositoryInterface {
|
||||
|
||||
/**
|
||||
* The purchase unit factory.
|
||||
*
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* CartRepository constructor.
|
||||
*
|
||||
* @param PurchaseUnitFactory $factory The purchase unit factory.
|
||||
*/
|
||||
public function __construct( PurchaseUnitFactory $factory ) {
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all Pur of the WooCommerce cart.
|
||||
*
|
||||
* @return PurchaseUnit[]
|
||||
*/
|
||||
public function all(): array {
|
||||
$cart = WC()->cart ?? new \WC_Cart();
|
||||
return array( $this->factory->from_wc_cart( $cart ) );
|
||||
}
|
||||
}
|
|
@ -72,7 +72,12 @@ class PartnerReferralsData {
|
|||
* @return array
|
||||
*/
|
||||
public function data(): array {
|
||||
return array(
|
||||
/**
|
||||
* Returns the partners referrals data.
|
||||
*/
|
||||
return apply_filters(
|
||||
'ppcp_partner_referrals_data',
|
||||
array(
|
||||
'partner_config_override' => array(
|
||||
'partner_logo_url' => 'https://connect.woocommerce.com/images/woocommerce_logo.png',
|
||||
/**
|
||||
|
@ -112,6 +117,7 @@ class PartnerReferralsData {
|
|||
'REFUND',
|
||||
'ADVANCED_TRANSACTIONS_SEARCH',
|
||||
'VAULT',
|
||||
'TRACKING_SHIPMENT_READWRITE',
|
||||
),
|
||||
'seller_nonce' => $this->nonce(),
|
||||
),
|
||||
|
@ -119,6 +125,7 @@ class PartnerReferralsData {
|
|||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* The Purchase Unit Repository interface.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
|
||||
/**
|
||||
* Interface PurchaseUnitRepositoryInterface
|
||||
*/
|
||||
interface PurchaseUnitRepositoryInterface {
|
||||
|
||||
|
||||
/**
|
||||
* Returns all purchase units.
|
||||
*
|
||||
* @return PurchaseUnit[]
|
||||
*/
|
||||
public function all(): array;
|
||||
}
|
|
@ -31,18 +31,20 @@ const bootstrap = () => {
|
|||
const onSmartButtonClick = (data, actions) => {
|
||||
window.ppcpFundingSource = data.fundingSource;
|
||||
|
||||
if (PayPalCommerceGateway.basic_checkout_validation_enabled) {
|
||||
// TODO: quick fix to get the error about empty form before attempting PayPal order
|
||||
// it should solve #513 for most of the users, but proper solution should be implemented later.
|
||||
const requiredFields = jQuery('form.woocommerce-checkout .validate-required:visible :input');
|
||||
requiredFields.each((i, input) => {
|
||||
jQuery(input).trigger('validate');
|
||||
});
|
||||
if (jQuery('form.woocommerce-checkout .woocommerce-invalid:visible').length) {
|
||||
if (jQuery('form.woocommerce-checkout .validate-required.woocommerce-invalid:visible').length) {
|
||||
errorHandler.clear();
|
||||
errorHandler.message(PayPalCommerceGateway.labels.error.js_validation);
|
||||
|
||||
return actions.reject();
|
||||
}
|
||||
}
|
||||
|
||||
const form = document.querySelector('form.woocommerce-checkout');
|
||||
if (form) {
|
||||
|
|
|
@ -41,7 +41,7 @@ class SingleProductBootstap {
|
|||
|
||||
}
|
||||
|
||||
priceAmountIsZero() {
|
||||
priceAmount() {
|
||||
|
||||
let priceText = "0";
|
||||
if (document.querySelector('form.cart ins .woocommerce-Price-amount')) {
|
||||
|
@ -55,9 +55,12 @@ class SingleProductBootstap {
|
|||
}
|
||||
|
||||
priceText = priceText.replace(/,/g, '.');
|
||||
const amount = parseFloat(priceText.replace(/([^\d,\.\s]*)/g, ''));
|
||||
return amount === 0;
|
||||
|
||||
return parseFloat(priceText.replace(/([^\d,\.\s]*)/g, ''));
|
||||
}
|
||||
|
||||
priceAmountIsZero() {
|
||||
return this.priceAmount() === 0;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -70,19 +73,12 @@ class SingleProductBootstap {
|
|||
() => {
|
||||
this.renderer.showButtons(this.gateway.button.wrapper);
|
||||
this.renderer.showButtons(this.gateway.hosted_fields.wrapper);
|
||||
let priceText = "0";
|
||||
if (document.querySelector('form.cart ins .woocommerce-Price-amount')) {
|
||||
priceText = document.querySelector('form.cart ins .woocommerce-Price-amount').innerText;
|
||||
}
|
||||
else if (document.querySelector('form.cart .woocommerce-Price-amount')) {
|
||||
priceText = document.querySelector('form.cart .woocommerce-Price-amount').innerText;
|
||||
}
|
||||
const amount = parseInt(priceText.replace(/([^\d,\.\s]*)/g, ''));
|
||||
this.messages.renderWithAmount(amount)
|
||||
this.messages.renderWithAmount(this.priceAmount())
|
||||
},
|
||||
() => {
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
this.messages.hideMessages();
|
||||
},
|
||||
document.querySelector('form.cart'),
|
||||
new ErrorHandler(this.gateway.labels.error.generic),
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
const dccInputFactory = (original) => {
|
||||
const styles = window.getComputedStyle(original);
|
||||
const newElement = document.createElement('span');
|
||||
|
||||
newElement.setAttribute('id', original.id);
|
||||
newElement.setAttribute('class', original.className);
|
||||
|
||||
Object.values(styles).forEach( (prop) => {
|
||||
if (! styles[prop] || ! isNaN(prop) ) {
|
||||
if (! styles[prop] || ! isNaN(prop) || prop === 'background-image' ) {
|
||||
return;
|
||||
}
|
||||
newElement.style.setProperty(prop,'' + styles[prop]);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import dccInputFactory from "../Helper/DccInputFactory";
|
||||
import {show} from "../Helper/Hiding";
|
||||
import Product from "../Entity/Product";
|
||||
|
||||
class CreditCardRenderer {
|
||||
|
||||
|
@ -117,11 +118,23 @@ class CreditCardRenderer {
|
|||
}
|
||||
const validCards = this.defaultConfig.hosted_fields.valid_cards;
|
||||
this.cardValid = validCards.indexOf(event.cards[0].type) !== -1;
|
||||
|
||||
const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type);
|
||||
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
||||
if (event.fields.number.isValid) {
|
||||
cardNumber.classList.add(className);
|
||||
}
|
||||
})
|
||||
hostedFields.on('validityChange', (event) => {
|
||||
const formValid = Object.keys(event.fields).every(function (key) {
|
||||
return event.fields[key].isValid;
|
||||
});
|
||||
|
||||
const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type);
|
||||
event.fields.number.isValid
|
||||
? cardNumber.classList.add(className)
|
||||
: this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
||||
|
||||
this.formValid = formValid;
|
||||
|
||||
});
|
||||
|
@ -230,5 +243,14 @@ class CreditCardRenderer {
|
|||
this.errorHandler.message(message);
|
||||
}
|
||||
}
|
||||
|
||||
_cardNumberFiledCLassNameByCardType(cardType) {
|
||||
return cardType === 'american-express' ? 'amex' : cardType.replace('-', '');
|
||||
}
|
||||
|
||||
_recreateElementClassAttribute(element, newClassName) {
|
||||
element.removeAttribute('class')
|
||||
element.setAttribute('class', newClassName);
|
||||
}
|
||||
}
|
||||
export default CreditCardRenderer;
|
||||
|
|
|
@ -88,6 +88,7 @@ return array(
|
|||
$settings_status,
|
||||
$currency,
|
||||
$container->get( 'wcgateway.all-funding-sources' ),
|
||||
$container->get( 'button.basic-checkout-validation-enabled' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
@ -107,14 +108,13 @@ return array(
|
|||
$cart = WC()->cart;
|
||||
$shipping = WC()->shipping();
|
||||
$request_data = $container->get( 'button.request-data' );
|
||||
$repository = $container->get( 'api.repository.cart' );
|
||||
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
|
||||
$data_store = \WC_Data_Store::load( 'product' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new ChangeCartEndpoint( $cart, $shipping, $request_data, $repository, $data_store, $logger );
|
||||
return new ChangeCartEndpoint( $cart, $shipping, $request_data, $purchase_unit_factory, $data_store, $logger );
|
||||
},
|
||||
'button.endpoint.create-order' => static function ( ContainerInterface $container ): CreateOrderEndpoint {
|
||||
$request_data = $container->get( 'button.request-data' );
|
||||
$cart_repository = $container->get( 'api.repository.cart' );
|
||||
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
|
||||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
$payer_factory = $container->get( 'api.factory.payer' );
|
||||
|
@ -125,8 +125,8 @@ return array(
|
|||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new CreateOrderEndpoint(
|
||||
$request_data,
|
||||
$cart_repository,
|
||||
$purchase_unit_factory,
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$order_endpoint,
|
||||
$payer_factory,
|
||||
$session_handler,
|
||||
|
@ -201,4 +201,12 @@ return array(
|
|||
return ! $container->get( 'button.is-logged-in' ) &&
|
||||
$container->get( 'button.registration-required' );
|
||||
},
|
||||
|
||||
'button.basic-checkout-validation-enabled' => static function ( ContainerInterface $container ): bool {
|
||||
/**
|
||||
* The filter allowing to disable the basic client-side validation of the checkout form
|
||||
* when the PayPal button is clicked.
|
||||
*/
|
||||
return (bool) apply_filters( 'woocommerce_paypal_payments_basic_checkout_validation_enabled', true );
|
||||
},
|
||||
);
|
||||
|
|
|
@ -144,6 +144,13 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $all_funding_sources;
|
||||
|
||||
/**
|
||||
* Whether the basic JS validation of the form iss enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $basic_checkout_validation_enabled;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -176,6 +183,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param array $all_funding_sources All existing funding sources.
|
||||
* @param bool $basic_checkout_validation_enabled Whether the basic JS validation of the form iss enabled.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -194,6 +202,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
SettingsStatus $settings_status,
|
||||
string $currency,
|
||||
array $all_funding_sources,
|
||||
bool $basic_checkout_validation_enabled,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
|
@ -212,6 +221,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->all_funding_sources = $all_funding_sources;
|
||||
$this->basic_checkout_validation_enabled = $basic_checkout_validation_enabled;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
@ -852,6 +862,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
'order_id' => 'pay-now' === $this->context() ? absint( $wp->query_vars['order-pay'] ) : 0,
|
||||
'single_product_buttons_enabled' => $this->settings->has( 'button_product_enabled' ) && $this->settings->get( 'button_product_enabled' ),
|
||||
'mini_cart_buttons_enabled' => $this->settings->has( 'button_mini-cart_enabled' ) && $this->settings->get( 'button_mini-cart_enabled' ),
|
||||
'basic_checkout_validation_enabled' => $this->basic_checkout_validation_enabled,
|
||||
);
|
||||
|
||||
if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) {
|
||||
|
@ -1036,6 +1047,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->context() === 'product'
|
||||
&& $this->settings->has( 'button_product_enabled' )
|
||||
&& $this->settings->get( 'button_product_enabled' )
|
||||
|| $this->settings->has( 'message_product_enabled' )
|
||||
) {
|
||||
$load_buttons = true;
|
||||
}
|
||||
|
@ -1049,6 +1061,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->context() === 'cart'
|
||||
&& $this->settings->has( 'button_cart_enabled' )
|
||||
&& $this->settings->get( 'button_cart_enabled' )
|
||||
|| $this->settings->has( 'message_product_enabled' )
|
||||
) {
|
||||
$load_buttons = true;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use Exception;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
|
@ -46,11 +46,11 @@ class ChangeCartEndpoint implements EndpointInterface {
|
|||
private $request_data;
|
||||
|
||||
/**
|
||||
* Contains purchase units based off the current WC cart.
|
||||
* The PurchaseUnit factory.
|
||||
*
|
||||
* @var CartRepository
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
private $repository;
|
||||
private $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The product data store.
|
||||
|
@ -72,7 +72,7 @@ class ChangeCartEndpoint implements EndpointInterface {
|
|||
* @param \WC_Cart $cart The current WC cart object.
|
||||
* @param \WC_Shipping $shipping The current WC shipping object.
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param CartRepository $repository The repository for the current purchase items.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The PurchaseUnit factory.
|
||||
* @param \WC_Data_Store $product_data_store The data store for products.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
|
@ -80,7 +80,7 @@ class ChangeCartEndpoint implements EndpointInterface {
|
|||
\WC_Cart $cart,
|
||||
\WC_Shipping $shipping,
|
||||
RequestData $request_data,
|
||||
CartRepository $repository,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
\WC_Data_Store $product_data_store,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
@ -88,7 +88,7 @@ class ChangeCartEndpoint implements EndpointInterface {
|
|||
$this->cart = $cart;
|
||||
$this->shipping = $shipping;
|
||||
$this->request_data = $request_data;
|
||||
$this->repository = $repository;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->product_data_store = $product_data_store;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
@ -292,11 +292,7 @@ class ChangeCartEndpoint implements EndpointInterface {
|
|||
* @return array
|
||||
*/
|
||||
private function generate_purchase_units(): array {
|
||||
return array_map(
|
||||
static function ( PurchaseUnit $line_item ): array {
|
||||
return $line_item->to_array();
|
||||
},
|
||||
$this->repository->all()
|
||||
);
|
||||
$pu = $this->purchase_unit_factory->from_wc_cart();
|
||||
return array( $pu->to_array() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
|
@ -48,13 +48,6 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The cart repository.
|
||||
*
|
||||
* @var CartRepository
|
||||
*/
|
||||
private $cart_repository;
|
||||
|
||||
/**
|
||||
* The PurchaseUnit factory.
|
||||
*
|
||||
|
@ -62,6 +55,13 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
*/
|
||||
private $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The shipping_preference factory.
|
||||
*
|
||||
* @var ShippingPreferenceFactory
|
||||
*/
|
||||
private $shipping_preference_factory;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
|
@ -105,11 +105,11 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
private $parsed_request_data;
|
||||
|
||||
/**
|
||||
* The array of purchase units for order.
|
||||
* The purchase unit for order.
|
||||
*
|
||||
* @var PurchaseUnit[]
|
||||
* @var PurchaseUnit|null
|
||||
*/
|
||||
private $purchase_units;
|
||||
private $purchase_unit;
|
||||
|
||||
/**
|
||||
* Whether a new user must be registered during checkout.
|
||||
|
@ -129,8 +129,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* CreateOrderEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The RequestData object.
|
||||
* @param CartRepository $cart_repository The CartRepository object.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The Purchaseunit factory.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The PurchaseUnit factory.
|
||||
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping_preference factory.
|
||||
* @param OrderEndpoint $order_endpoint The OrderEndpoint object.
|
||||
* @param PayerFactory $payer_factory The PayerFactory object.
|
||||
* @param SessionHandler $session_handler The SessionHandler object.
|
||||
|
@ -141,8 +141,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
CartRepository $cart_repository,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
ShippingPreferenceFactory $shipping_preference_factory,
|
||||
OrderEndpoint $order_endpoint,
|
||||
PayerFactory $payer_factory,
|
||||
SessionHandler $session_handler,
|
||||
|
@ -153,8 +153,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
) {
|
||||
|
||||
$this->request_data = $request_data;
|
||||
$this->cart_repository = $cart_repository;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->shipping_preference_factory = $shipping_preference_factory;
|
||||
$this->api_endpoint = $order_endpoint;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->session_handler = $session_handler;
|
||||
|
@ -198,9 +198,9 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
)
|
||||
);
|
||||
}
|
||||
$this->purchase_units = array( $this->purchase_unit_factory->from_wc_order( $wc_order ) );
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
} else {
|
||||
$this->purchase_units = $this->cart_repository->all();
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart();
|
||||
|
||||
// The cart does not have any info about payment method, so we must handle free trial here.
|
||||
if ( (
|
||||
|
@ -209,10 +209,10 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
)
|
||||
&& $this->is_free_trial_cart()
|
||||
) {
|
||||
$this->purchase_units[0]->set_amount(
|
||||
$this->purchase_unit->set_amount(
|
||||
new Amount(
|
||||
new Money( 1.0, $this->purchase_units[0]->amount()->currency_code() ),
|
||||
$this->purchase_units[0]->amount()->breakdown()
|
||||
new Money( 1.0, $this->purchase_unit->amount()->currency_code() ),
|
||||
$this->purchase_unit->amount()->breakdown()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -329,17 +329,22 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* 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'];
|
||||
assert( $this->purchase_unit instanceof PurchaseUnit );
|
||||
|
||||
$shipping_preference = $this->shipping_preference_factory->from_state(
|
||||
$this->purchase_unit,
|
||||
$this->parsed_request_data['context'],
|
||||
WC()->cart,
|
||||
$this->parsed_request_data['funding_source'] ?? ''
|
||||
);
|
||||
|
||||
try {
|
||||
return $this->api_endpoint->create(
|
||||
$this->purchase_units,
|
||||
array( $this->purchase_unit ),
|
||||
$shipping_preference,
|
||||
$this->payer( $this->parsed_request_data, $wc_order ),
|
||||
null,
|
||||
$this->payment_method(),
|
||||
'',
|
||||
$shipping_address_is_fix
|
||||
$this->payment_method()
|
||||
);
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
// Looks like currently there is no proper way to validate the shipping address for PayPal,
|
||||
|
@ -354,17 +359,14 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
) ) {
|
||||
$this->logger->info( 'Invalid shipping address for order creation, retrying without it.' );
|
||||
|
||||
foreach ( $this->purchase_units as $purchase_unit ) {
|
||||
$purchase_unit->set_shipping( null );
|
||||
}
|
||||
$this->purchase_unit->set_shipping( null );
|
||||
|
||||
return $this->api_endpoint->create(
|
||||
$this->purchase_units,
|
||||
array( $this->purchase_unit ),
|
||||
$shipping_preference,
|
||||
$this->payer( $this->parsed_request_data, $wc_order ),
|
||||
null,
|
||||
$this->payment_method(),
|
||||
'',
|
||||
$shipping_address_is_fix
|
||||
$this->payment_method()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -449,12 +451,11 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
/**
|
||||
* Checks whether the terms input field is checked.
|
||||
*
|
||||
* @param string $form_values The form values.
|
||||
* @param array $form_fields The form fields.
|
||||
* @throws \RuntimeException When field is not checked.
|
||||
*/
|
||||
private function validate_paynow_form( string $form_values ) {
|
||||
$parsed_values = wp_parse_args( $form_values );
|
||||
if ( isset( $parsed_values['terms-field'] ) && ! isset( $parsed_values['terms'] ) ) {
|
||||
private function validate_paynow_form( array $form_fields ) {
|
||||
if ( isset( $form_fields['terms-field'] ) && ! isset( $form_fields['terms'] ) ) {
|
||||
throw new \RuntimeException(
|
||||
__( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
|
|
|
@ -70,6 +70,38 @@ const ppcp_onboarding = {
|
|||
},
|
||||
1000
|
||||
);
|
||||
|
||||
const onboard_pui = document.querySelector('#ppcp-onboarding-pui');
|
||||
onboard_pui?.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
buttons.forEach((element) => {
|
||||
element.removeAttribute('href');
|
||||
});
|
||||
|
||||
fetch(PayPalCommerceGatewayOnboarding.pui_endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
nonce: PayPalCommerceGatewayOnboarding.pui_nonce,
|
||||
checked: onboard_pui.checked
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
}).then((data)=>{
|
||||
if (!data.success) {
|
||||
alert('Could not update signup buttons: ' + JSON.stringify(data));
|
||||
return;
|
||||
}
|
||||
|
||||
buttons.forEach((element) => {
|
||||
for (let [key, value] of Object.entries(data.data.signup_links)) {
|
||||
key = 'connect-to' + key.replace(/-/g, '');
|
||||
if(key === element.id) {
|
||||
element.setAttribute('href', value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
loginSeller: function(env, authCode, sharedId) {
|
||||
|
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Assets\OnboardingAssets;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Endpoint\PayUponInvoiceEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\OnboardingRESTController;
|
||||
|
@ -186,6 +187,16 @@ return array(
|
|||
$logger
|
||||
);
|
||||
},
|
||||
'onboarding.endpoint.pui' => static function( ContainerInterface $container ) : PayUponInvoiceEndpoint {
|
||||
return new PayUponInvoiceEndpoint(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'onboarding.signup-link-cache' ),
|
||||
$container->get( 'onboarding.render' ),
|
||||
$container->get( 'onboarding.signup-link-ids' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.partner-referrals-sandbox' => static function ( ContainerInterface $container ) : PartnerReferrals {
|
||||
|
||||
return new PartnerReferrals(
|
||||
|
@ -202,23 +213,36 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'onboarding.signup-link-cache' => static function( ContainerInterface $container ): Cache {
|
||||
return new Cache( 'ppcp-paypal-signup-link' );
|
||||
},
|
||||
'onboarding.signup-link-ids' => static function ( ContainerInterface $container ): array {
|
||||
return array(
|
||||
'production-ppcp',
|
||||
'production-express_checkout',
|
||||
'sandbox-ppcp',
|
||||
'sandbox-express_checkout',
|
||||
);
|
||||
},
|
||||
'onboarding.render' => static function ( ContainerInterface $container ) : OnboardingRenderer {
|
||||
|
||||
$partner_referrals = $container->get( 'api.endpoint.partner-referrals-production' );
|
||||
$partner_referrals_sandbox = $container->get( 'api.endpoint.partner-referrals-sandbox' );
|
||||
$partner_referrals_data = $container->get( 'api.repository.partner-referrals-data' );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$signup_link_cache = $container->get( 'onboarding.signup-link-cache' );
|
||||
return new OnboardingRenderer(
|
||||
$settings,
|
||||
$partner_referrals,
|
||||
$partner_referrals_sandbox,
|
||||
$partner_referrals_data
|
||||
$partner_referrals_data,
|
||||
$signup_link_cache
|
||||
);
|
||||
},
|
||||
'onboarding.render-options' => static function ( ContainerInterface $container ) : OnboardingOptionsRenderer {
|
||||
return new OnboardingOptionsRenderer(
|
||||
$container->get( 'onboarding.url' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
$container->get( 'api.shop.country' ),
|
||||
$container->get( 'wcgateway.settings' )
|
||||
);
|
||||
},
|
||||
'onboarding.rest' => static function( $container ) : OnboardingRESTController {
|
||||
|
|
|
@ -145,6 +145,8 @@ class OnboardingAssets {
|
|||
'error_messages' => array(
|
||||
'no_credentials' => __( 'API credentials must be entered to save the settings.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'pui_endpoint' => \WC_AJAX::get_endpoint( 'ppc-pui' ),
|
||||
'pui_nonce' => wp_create_nonce( 'ppc-pui' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
$is_sandbox = isset( $data['env'] ) && 'sandbox' === $data['env'];
|
||||
$this->settings->set( 'sandbox_on', $is_sandbox );
|
||||
$this->settings->set( 'products_dcc_enabled', null );
|
||||
$this->settings->set( 'products_pui_enabled', null );
|
||||
$this->settings->persist();
|
||||
|
||||
$endpoint = $is_sandbox ? $this->login_seller_sandbox : $this->login_seller_production;
|
||||
|
@ -144,6 +145,7 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
}
|
||||
$this->settings->set( 'client_secret', $credentials->client_secret );
|
||||
$this->settings->set( 'client_id', $credentials->client_id );
|
||||
$this->settings->persist();
|
||||
|
||||
$accept_cards = (bool) ( $data['acceptCards'] ?? true );
|
||||
$funding_sources = array();
|
||||
|
|
143
modules/ppcp-onboarding/src/Endpoint/PayUponInvoiceEndpoint.php
Normal file
143
modules/ppcp-onboarding/src/Endpoint/PayUponInvoiceEndpoint.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the onboard with Pay upon Invoice setting.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Onboarding\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Onboarding\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class PayUponInvoiceEndpoint
|
||||
*/
|
||||
class PayUponInvoiceEndpoint implements EndpointInterface {
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The request data.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
protected $request_data;
|
||||
|
||||
/**
|
||||
* The signup link cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
protected $signup_link_cache;
|
||||
|
||||
/**
|
||||
* The onboarding renderer.
|
||||
*
|
||||
* @var OnboardingRenderer
|
||||
*/
|
||||
protected $onboarding_renderer;
|
||||
|
||||
/**
|
||||
* Signup link ids.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $signup_link_ids;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* PayUponInvoiceEndpoint constructor.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
* @param RequestData $request_data The request data.
|
||||
* @param Cache $signup_link_cache The signup link cache.
|
||||
* @param OnboardingRenderer $onboarding_renderer The onboarding renderer.
|
||||
* @param array $signup_link_ids Signup link ids.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
RequestData $request_data,
|
||||
Cache $signup_link_cache,
|
||||
OnboardingRenderer $onboarding_renderer,
|
||||
array $signup_link_ids,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->request_data = $request_data;
|
||||
$this->signup_link_cache = $signup_link_cache;
|
||||
$this->onboarding_renderer = $onboarding_renderer;
|
||||
$this->logger = $logger;
|
||||
$this->signup_link_ids = $signup_link_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return 'ppc-pui';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException When order not found or handling failed.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
$signup_links = array();
|
||||
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
$this->settings->set( 'ppcp-onboarding-pui', $data['checked'] );
|
||||
$this->settings->persist();
|
||||
|
||||
foreach ( $this->signup_link_ids as $key ) {
|
||||
if ( $this->signup_link_cache->has( $key ) ) {
|
||||
$this->signup_link_cache->delete( $key );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $this->signup_link_ids as $key ) {
|
||||
$parts = explode( '-', $key );
|
||||
$is_production = 'production' === $parts[0];
|
||||
$products = 'ppcp' === $parts[1] ? array( 'PPCP' ) : array( 'EXPRESS_CHECKOUT' );
|
||||
$signup_links[ $key ] = $this->onboarding_renderer->get_signup_link( $is_production, $products );
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( $exception->getMessage() );
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'onboarding_pui' => $this->settings->get( 'ppcp-onboarding-pui' ),
|
||||
'signup_links' => $signup_links,
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +96,14 @@ class OnboardingModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_ppc-pui',
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'onboarding.endpoint.pui' );
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
// Initialize REST routes at the appropriate time.
|
||||
$rest_controller = $c->get( 'onboarding.rest' );
|
||||
add_action( 'rest_api_init', array( $rest_controller, 'register_routes' ) );
|
||||
|
|
|
@ -236,6 +236,7 @@ class OnboardingRESTController {
|
|||
}
|
||||
|
||||
$settings->set( 'products_dcc_enabled', null );
|
||||
$settings->set( 'products_pui_enabled', null );
|
||||
|
||||
if ( ! $settings->persist() ) {
|
||||
return new \WP_Error(
|
||||
|
|
|
@ -9,6 +9,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Onboarding\Render;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class OnboardingRenderer
|
||||
*/
|
||||
|
@ -27,15 +30,24 @@ class OnboardingOptionsRenderer {
|
|||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* OnboardingOptionsRenderer constructor.
|
||||
*
|
||||
* @param string $module_url The module url (for assets).
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
* @param Settings $settings The settings.
|
||||
*/
|
||||
public function __construct( string $module_url, string $country ) {
|
||||
public function __construct( string $module_url, string $country, Settings $settings ) {
|
||||
$this->module_url = $module_url;
|
||||
$this->country = $country;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,8 +68,29 @@ class OnboardingOptionsRenderer {
|
|||
__( 'Securely accept all major credit & debit cards on the strength of the PayPal network', 'woocommerce-paypal-payments' ) . '
|
||||
</label>
|
||||
</li>
|
||||
<li>' . $this->render_dcc( $is_shop_supports_dcc ) . '</li>
|
||||
</ul>';
|
||||
<li>' . $this->render_dcc( $is_shop_supports_dcc ) . '</li>' .
|
||||
$this->render_pui_option()
|
||||
. '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders pui option.
|
||||
*
|
||||
* @return string
|
||||
* @throws NotFoundException When setting is not found.
|
||||
*/
|
||||
private function render_pui_option(): string {
|
||||
if ( 'DE' === $this->country ) {
|
||||
$checked = 'checked';
|
||||
if ( $this->settings->has( 'ppcp-onboarding-pui' ) && $this->settings->get( 'ppcp-onboarding-pui' ) !== '1' ) {
|
||||
$checked = '';
|
||||
}
|
||||
return '<li><label><input type="checkbox" id="ppcp-onboarding-pui" ' . $checked . '> ' .
|
||||
__( 'Onboard with Pay upon Invoice', 'woocommerce-paypal-payments' ) . '
|
||||
</label></li>';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Onboarding\Render;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
|
@ -47,6 +48,13 @@ class OnboardingRenderer {
|
|||
*/
|
||||
private $partner_referrals_data;
|
||||
|
||||
/**
|
||||
* The cache
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* OnboardingRenderer constructor.
|
||||
*
|
||||
|
@ -54,17 +62,20 @@ class OnboardingRenderer {
|
|||
* @param PartnerReferrals $production_partner_referrals The PartnerReferrals for production.
|
||||
* @param PartnerReferrals $sandbox_partner_referrals The PartnerReferrals for sandbox.
|
||||
* @param PartnerReferralsData $partner_referrals_data The default partner referrals data.
|
||||
* @param Cache $cache The cache.
|
||||
*/
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
PartnerReferrals $production_partner_referrals,
|
||||
PartnerReferrals $sandbox_partner_referrals,
|
||||
PartnerReferralsData $partner_referrals_data
|
||||
PartnerReferralsData $partner_referrals_data,
|
||||
Cache $cache
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->production_partner_referrals = $production_partner_referrals;
|
||||
$this->sandbox_partner_referrals = $sandbox_partner_referrals;
|
||||
$this->partner_referrals_data = $partner_referrals_data;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,9 +94,17 @@ class OnboardingRenderer {
|
|||
->with_products( $products )
|
||||
->data();
|
||||
|
||||
$environment = $is_production ? 'production' : 'sandbox';
|
||||
$product = 'PPCP' === $data['products'][0] ? 'ppcp' : 'express_checkout';
|
||||
if ( $this->cache->has( $environment . '-' . $product ) ) {
|
||||
return $this->cache->get( $environment . '-' . $product );
|
||||
}
|
||||
|
||||
$url = $is_production ? $this->production_partner_referrals->signup_link( $data ) : $this->sandbox_partner_referrals->signup_link( $data );
|
||||
$url = add_query_arg( $args, $url );
|
||||
|
||||
$this->cache->set( $environment . '-' . $product, $url, 3 * MONTH_IN_SECONDS );
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ return array(
|
|||
$repository,
|
||||
$endpoint,
|
||||
$purchase_unit_factory,
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$payer_factory,
|
||||
$environment
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -58,6 +59,13 @@ class RenewalHandler {
|
|||
*/
|
||||
private $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The shipping_preference factory.
|
||||
*
|
||||
* @var ShippingPreferenceFactory
|
||||
*/
|
||||
private $shipping_preference_factory;
|
||||
|
||||
/**
|
||||
* The payer factory.
|
||||
*
|
||||
|
@ -79,6 +87,7 @@ class RenewalHandler {
|
|||
* @param PaymentTokenRepository $repository The payment token repository.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping_preference factory.
|
||||
* @param PayerFactory $payer_factory The payer factory.
|
||||
* @param Environment $environment The environment.
|
||||
*/
|
||||
|
@ -87,6 +96,7 @@ class RenewalHandler {
|
|||
PaymentTokenRepository $repository,
|
||||
OrderEndpoint $order_endpoint,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
ShippingPreferenceFactory $shipping_preference_factory,
|
||||
PayerFactory $payer_factory,
|
||||
Environment $environment
|
||||
) {
|
||||
|
@ -95,6 +105,7 @@ class RenewalHandler {
|
|||
$this->repository = $repository;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->shipping_preference_factory = $shipping_preference_factory;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
@ -133,7 +144,7 @@ class RenewalHandler {
|
|||
*
|
||||
* @throws \Exception If customer cannot be read/found.
|
||||
*/
|
||||
private function process_order( \WC_Order $wc_order ) {
|
||||
private function process_order( \WC_Order $wc_order ): void {
|
||||
|
||||
$user_id = (int) $wc_order->get_customer_id();
|
||||
$customer = new \WC_Customer( $user_id );
|
||||
|
@ -143,9 +154,14 @@ class RenewalHandler {
|
|||
}
|
||||
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
$payer = $this->payer_factory->from_customer( $customer );
|
||||
$shipping_preference = $this->shipping_preference_factory->from_state(
|
||||
$purchase_unit,
|
||||
'renewal'
|
||||
);
|
||||
|
||||
$order = $this->order_endpoint->create(
|
||||
array( $purchase_unit ),
|
||||
$shipping_preference,
|
||||
$payer,
|
||||
$token
|
||||
);
|
||||
|
|
55
modules/ppcp-wc-gateway/resources/js/pay-upon-invoice.js
Normal file
55
modules/ppcp-wc-gateway/resources/js/pay-upon-invoice.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
window.addEventListener('load', function() {
|
||||
|
||||
function _loadBeaconJS(options) {
|
||||
var script = document.createElement('script');
|
||||
script.src = options.fnUrl;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
function _injectConfig() {
|
||||
var script = document.querySelector("[fncls='fnparams-dede7cc5-15fd-4c75-a9f4-36c430ee3a99']");
|
||||
if (script) {
|
||||
if (script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
script = document.createElement('script');
|
||||
script.id = 'fconfig';
|
||||
script.type = 'application/json';
|
||||
script.setAttribute('fncls', 'fnparams-dede7cc5-15fd-4c75-a9f4-36c430ee3a99');
|
||||
|
||||
var configuration = {
|
||||
'f': FraudNetConfig.f,
|
||||
's': FraudNetConfig.s
|
||||
};
|
||||
if(FraudNetConfig.sandbox === '1') {
|
||||
configuration.sandbox = true;
|
||||
}
|
||||
|
||||
script.text = JSON.stringify(configuration);
|
||||
document.body.appendChild(script);
|
||||
|
||||
const payForOrderForm = document.forms.order_review;
|
||||
if(payForOrderForm) {
|
||||
const puiPayForOrderSessionId = document.createElement('input');
|
||||
puiPayForOrderSessionId.setAttribute('type', 'hidden');
|
||||
puiPayForOrderSessionId.setAttribute('name', 'pui_pay_for_order_session_id');
|
||||
puiPayForOrderSessionId.setAttribute('value', FraudNetConfig.f);
|
||||
payForOrderForm.appendChild(puiPayForOrderSessionId);
|
||||
}
|
||||
|
||||
_loadBeaconJS({fnUrl: "https://c.paypal.com/da/r/fb.js"})
|
||||
}
|
||||
|
||||
document.addEventListener('hosted_fields_loaded', (event) => {
|
||||
if (PAYPAL.asyncData && typeof PAYPAL.asyncData.initAndCollect === 'function') {
|
||||
PAYPAL.asyncData.initAndCollect()
|
||||
}
|
||||
|
||||
_injectConfig();
|
||||
});
|
||||
|
||||
_injectConfig();
|
||||
})
|
||||
|
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\WcGateway;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
|
@ -30,8 +31,16 @@ use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNetSessionId;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNetSourceWebsiteId;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSourceFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoice;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
|
||||
|
@ -63,6 +72,7 @@ return array(
|
|||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
$environment = $container->get( 'onboarding.environment' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
$api_shop_country = $container->get( 'api.shop.country' );
|
||||
return new PayPalGateway(
|
||||
$settings_renderer,
|
||||
$funding_source_renderer,
|
||||
|
@ -77,9 +87,11 @@ return array(
|
|||
$page_id,
|
||||
$environment,
|
||||
$payment_token_repository,
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$logger,
|
||||
$payments_endpoint,
|
||||
$order_endpoint
|
||||
$order_endpoint,
|
||||
$api_shop_country
|
||||
);
|
||||
},
|
||||
'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway {
|
||||
|
@ -112,6 +124,7 @@ return array(
|
|||
$transaction_url_provider,
|
||||
$payment_token_repository,
|
||||
$purchase_unit_factory,
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$payer_factory,
|
||||
$order_endpoint,
|
||||
$subscription_helper,
|
||||
|
@ -137,7 +150,7 @@ return array(
|
|||
}
|
||||
|
||||
$section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : '';
|
||||
return in_array( $section, array( PayPalGateway::ID, CreditCardGateway::ID, WebhooksStatusPage::ID ), true );
|
||||
return in_array( $section, array( PayPalGateway::ID, CreditCardGateway::ID, WebhooksStatusPage::ID, PayUponInvoiceGateway::ID ), true );
|
||||
},
|
||||
|
||||
'wcgateway.current-ppcp-settings-page-id' => static function ( ContainerInterface $container ): string {
|
||||
|
@ -171,7 +184,10 @@ return array(
|
|||
return new AuthorizeOrderActionNotice();
|
||||
},
|
||||
'wcgateway.settings.sections-renderer' => static function ( ContainerInterface $container ): SectionsRenderer {
|
||||
return new SectionsRenderer( $container->get( 'wcgateway.current-ppcp-settings-page-id' ) );
|
||||
return new SectionsRenderer(
|
||||
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
'wcgateway.settings.status' => static function ( ContainerInterface $container ): SettingsStatus {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
@ -205,7 +221,19 @@ return array(
|
|||
$cache = new Cache( 'ppcp-paypal-bearer' );
|
||||
$bearer = $container->get( 'api.bearer' );
|
||||
$page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
|
||||
return new SettingsListener( $settings, $fields, $webhook_registrar, $cache, $state, $bearer, $page_id );
|
||||
$signup_link_cache = $container->get( 'onboarding.signup-link-cache' );
|
||||
$signup_link_ids = $container->get( 'onboarding.signup-link-ids' );
|
||||
return new SettingsListener(
|
||||
$settings,
|
||||
$fields,
|
||||
$webhook_registrar,
|
||||
$cache,
|
||||
$state,
|
||||
$bearer,
|
||||
$page_id,
|
||||
$signup_link_cache,
|
||||
$signup_link_ids
|
||||
);
|
||||
},
|
||||
'wcgateway.order-processor' => static function ( ContainerInterface $container ): OrderProcessor {
|
||||
|
||||
|
@ -2129,7 +2157,69 @@ return array(
|
|||
$container->get( 'wcgateway.settings' )
|
||||
);
|
||||
},
|
||||
|
||||
'wcgateway.pay-upon-invoice-order-endpoint' => static function ( ContainerInterface $container ): PayUponInvoiceOrderEndpoint {
|
||||
return new PayUponInvoiceOrderEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.factory.order' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-fraudnet' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-payment-source-factory' => static function ( ContainerInterface $container ): PaymentSourceFactory {
|
||||
return new PaymentSourceFactory();
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-gateway' => static function ( ContainerInterface $container ): PayUponInvoiceGateway {
|
||||
return new PayUponInvoiceGateway(
|
||||
$container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ),
|
||||
$container->get( 'api.factory.purchase-unit' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-payment-source-factory' ),
|
||||
$container->get( 'onboarding.environment' ),
|
||||
$container->get( 'wcgateway.transaction-url-provider' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-helper' )
|
||||
);
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-fraudnet-session-id' => static function ( ContainerInterface $container ): FraudNetSessionId {
|
||||
return new FraudNetSessionId();
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-fraudnet-source-website-id' => static function ( ContainerInterface $container ): FraudNetSourceWebsiteId {
|
||||
return new FraudNetSourceWebsiteId( $container->get( 'api.merchant_id' ) );
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-fraudnet' => static function ( ContainerInterface $container ): FraudNet {
|
||||
$session_id = $container->get( 'wcgateway.pay-upon-invoice-fraudnet-session-id' );
|
||||
$source_website_id = $container->get( 'wcgateway.pay-upon-invoice-fraudnet-source-website-id' );
|
||||
return new FraudNet(
|
||||
(string) $session_id(),
|
||||
(string) $source_website_id()
|
||||
);
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-helper' => static function( ContainerInterface $container ): PayUponInvoiceHelper {
|
||||
return new PayUponInvoiceHelper();
|
||||
},
|
||||
'wcgateway.pay-upon-invoice-product-status' => static function( ContainerInterface $container ): PayUponInvoiceProductStatus {
|
||||
return new PayUponInvoiceProductStatus(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'api.endpoint.partners' )
|
||||
);
|
||||
},
|
||||
'wcgateway.pay-upon-invoice' => static function ( ContainerInterface $container ): PayUponInvoice {
|
||||
return new PayUponInvoice(
|
||||
$container->get( 'wcgateway.url' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-fraudnet' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'onboarding.environment' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'onboarding.state' ),
|
||||
$container->get( 'wcgateway.is-ppcp-settings-page' ),
|
||||
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-product-status' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-helper' ),
|
||||
$container->get( 'api.factory.capture' )
|
||||
);
|
||||
},
|
||||
'wcgateway.logging.is-enabled' => function ( ContainerInterface $container ) : bool {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
|
@ -111,6 +112,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
*/
|
||||
private $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The shipping_preference factory.
|
||||
*
|
||||
* @var ShippingPreferenceFactory
|
||||
*/
|
||||
private $shipping_preference_factory;
|
||||
|
||||
/**
|
||||
* The payer factory.
|
||||
*
|
||||
|
@ -167,6 +175,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
* @param TransactionUrlProvider $transaction_url_provider Service able to provide view transaction url base.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping_preference factory.
|
||||
* @param PayerFactory $payer_factory The payer factory.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
|
@ -186,6 +195,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
TransactionUrlProvider $transaction_url_provider,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
ShippingPreferenceFactory $shipping_preference_factory,
|
||||
PayerFactory $payer_factory,
|
||||
OrderEndpoint $order_endpoint,
|
||||
SubscriptionHelper $subscription_helper,
|
||||
|
@ -255,6 +265,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
$this->module_url = $module_url;
|
||||
$this->payment_token_repository = $payment_token_repository;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->shipping_preference_factory = $shipping_preference_factory;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->transaction_url_provider = $transaction_url_provider;
|
||||
|
|
|
@ -12,13 +12,14 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
@ -118,6 +119,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
*/
|
||||
protected $payment_token_repository;
|
||||
|
||||
/**
|
||||
* The shipping_preference factory.
|
||||
*
|
||||
* @var ShippingPreferenceFactory
|
||||
*/
|
||||
private $shipping_preference_factory;
|
||||
|
||||
/**
|
||||
* The payments endpoint
|
||||
*
|
||||
|
@ -160,6 +168,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The api shop country.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $api_shop_country;
|
||||
|
||||
/**
|
||||
* PayPalGateway constructor.
|
||||
*
|
||||
|
@ -176,9 +191,11 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param Environment $environment The environment.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping_preference factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param PaymentsEndpoint $payments_endpoint The payments endpoint.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param string $api_shop_country The api shop country.
|
||||
*/
|
||||
public function __construct(
|
||||
SettingsRenderer $settings_renderer,
|
||||
|
@ -194,9 +211,11 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
string $page_id,
|
||||
Environment $environment,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
ShippingPreferenceFactory $shipping_preference_factory,
|
||||
LoggerInterface $logger,
|
||||
PaymentsEndpoint $payments_endpoint,
|
||||
OrderEndpoint $order_endpoint
|
||||
OrderEndpoint $order_endpoint,
|
||||
string $api_shop_country
|
||||
) {
|
||||
|
||||
$this->id = self::ID;
|
||||
|
@ -214,6 +233,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$this->id = self::ID;
|
||||
$this->order_processor = $order_processor;
|
||||
$this->authorized_payments = $authorized_payments_processor;
|
||||
$this->shipping_preference_factory = $shipping_preference_factory;
|
||||
$this->settings_renderer = $settings_renderer;
|
||||
$this->config = $config;
|
||||
$this->session_handler = $session_handler;
|
||||
|
@ -277,6 +297,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$this->payments_endpoint = $payments_endpoint;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->state = $state;
|
||||
$this->api_shop_country = $api_shop_country;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -342,6 +363,10 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
if ( $this->is_paypal_tab() ) {
|
||||
return __( 'PayPal Checkout', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
if ( $this->is_pui_tab() ) {
|
||||
return __( 'Pay upon Invoice', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
|
||||
return __( 'PayPal', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
|
||||
|
@ -390,6 +415,19 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we are on the PUI tab.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_pui_tab():bool {
|
||||
if ( 'DE' !== $this->api_shop_country ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_admin() && PayUponInvoiceGateway::ID === $this->page_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we are on the Webhooks Status tab.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/**
|
||||
* Fraudnet entity.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
/**
|
||||
* Class FraudNet
|
||||
*/
|
||||
class FraudNet {
|
||||
|
||||
/**
|
||||
* The session ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $session_id;
|
||||
|
||||
/**
|
||||
* The source website ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $source_website_id;
|
||||
|
||||
/**
|
||||
* FraudNet constructor.
|
||||
*
|
||||
* @param string $session_id The session ID.
|
||||
* @param string $source_website_id The source website ID.
|
||||
*/
|
||||
public function __construct( string $session_id, string $source_website_id ) {
|
||||
$this->session_id = $session_id;
|
||||
$this->source_website_id = $source_website_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function session_id(): string {
|
||||
return $this->session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source website id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function source_website_id(): string {
|
||||
return $this->source_website_id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
/**
|
||||
* Fraudnet session id.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class FraudNetSessionId.
|
||||
*/
|
||||
class FraudNetSessionId {
|
||||
|
||||
/**
|
||||
* Generates a session ID or use the existing one from WC session.
|
||||
*
|
||||
* @return array|string
|
||||
* @throws Exception When there is a problem with the session ID.
|
||||
*/
|
||||
public function __invoke() {
|
||||
if ( WC()->session === null ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( WC()->session->get( 'ppcp_fraudnet_session_id' ) ) {
|
||||
return WC()->session->get( 'ppcp_fraudnet_session_id' );
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) {
|
||||
$pui_pay_for_order_session_id = filter_input( INPUT_POST, 'pui_pay_for_order_session_id', FILTER_SANITIZE_STRING );
|
||||
if ( $pui_pay_for_order_session_id && '' !== $pui_pay_for_order_session_id ) {
|
||||
return $pui_pay_for_order_session_id;
|
||||
}
|
||||
}
|
||||
|
||||
$session_id = bin2hex( random_bytes( 16 ) );
|
||||
WC()->session->set( 'ppcp_fraudnet_session_id', $session_id );
|
||||
|
||||
return $session_id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* Fraudnet source website ID.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
/**
|
||||
* Class FraudNetSourceWebsiteId.
|
||||
*/
|
||||
class FraudNetSourceWebsiteId {
|
||||
|
||||
/**
|
||||
* The merchant id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $api_merchant_id;
|
||||
|
||||
/**
|
||||
* FraudNetSourceWebsiteId constructor.
|
||||
*
|
||||
* @param string $api_merchant_id The merchant id.
|
||||
*/
|
||||
public function __construct( string $api_merchant_id ) {
|
||||
$this->api_merchant_id = $api_merchant_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source website ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __invoke() {
|
||||
return "{$this->api_merchant_id}_checkout-page";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,538 @@
|
|||
<?php
|
||||
/**
|
||||
* PUI integration.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class PayUponInvoice.
|
||||
*/
|
||||
class PayUponInvoice {
|
||||
|
||||
use TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* The module URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $module_url;
|
||||
|
||||
/**
|
||||
* The FraudNet entity.
|
||||
*
|
||||
* @var FraudNet
|
||||
*/
|
||||
protected $fraud_net;
|
||||
|
||||
/**
|
||||
* The pui order endpoint.
|
||||
*
|
||||
* @var PayUponInvoiceOrderEndpoint
|
||||
*/
|
||||
protected $pui_order_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The environment.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
protected $environment;
|
||||
|
||||
/**
|
||||
* The asset version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $asset_version;
|
||||
|
||||
/**
|
||||
* The PUI helper.
|
||||
*
|
||||
* @var PayUponInvoiceHelper
|
||||
*/
|
||||
protected $pui_helper;
|
||||
|
||||
/**
|
||||
* The onboarding state.
|
||||
*
|
||||
* @var State
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Whether the current page is the PPCP settings page.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_ppcp_settings_page;
|
||||
|
||||
/**
|
||||
* Current PayPal settings page id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $current_ppcp_settings_page_id;
|
||||
|
||||
/**
|
||||
* PUI seller product status.
|
||||
*
|
||||
* @var PayUponInvoiceProductStatus
|
||||
*/
|
||||
protected $pui_product_status;
|
||||
|
||||
/**
|
||||
* The capture factory.
|
||||
*
|
||||
* @var CaptureFactory
|
||||
*/
|
||||
protected $capture_factory;
|
||||
|
||||
/**
|
||||
* PayUponInvoice constructor.
|
||||
*
|
||||
* @param string $module_url The module URL.
|
||||
* @param FraudNet $fraud_net The FraudNet entity.
|
||||
* @param PayUponInvoiceOrderEndpoint $pui_order_endpoint The PUI order endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param Settings $settings The settings.
|
||||
* @param Environment $environment The environment.
|
||||
* @param string $asset_version The asset version.
|
||||
* @param State $state The onboarding state.
|
||||
* @param bool $is_ppcp_settings_page Whether page is PayPal settings poge.
|
||||
* @param string $current_ppcp_settings_page_id Current PayPal settings page id.
|
||||
* @param PayUponInvoiceProductStatus $pui_product_status The PUI product status.
|
||||
* @param PayUponInvoiceHelper $pui_helper The PUI helper.
|
||||
* @param CaptureFactory $capture_factory The capture factory.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
FraudNet $fraud_net,
|
||||
PayUponInvoiceOrderEndpoint $pui_order_endpoint,
|
||||
LoggerInterface $logger,
|
||||
Settings $settings,
|
||||
Environment $environment,
|
||||
string $asset_version,
|
||||
State $state,
|
||||
bool $is_ppcp_settings_page,
|
||||
string $current_ppcp_settings_page_id,
|
||||
PayUponInvoiceProductStatus $pui_product_status,
|
||||
PayUponInvoiceHelper $pui_helper,
|
||||
CaptureFactory $capture_factory
|
||||
) {
|
||||
$this->module_url = $module_url;
|
||||
$this->fraud_net = $fraud_net;
|
||||
$this->pui_order_endpoint = $pui_order_endpoint;
|
||||
$this->logger = $logger;
|
||||
$this->settings = $settings;
|
||||
$this->environment = $environment;
|
||||
$this->asset_version = $asset_version;
|
||||
$this->state = $state;
|
||||
$this->is_ppcp_settings_page = $is_ppcp_settings_page;
|
||||
$this->current_ppcp_settings_page_id = $current_ppcp_settings_page_id;
|
||||
$this->pui_product_status = $pui_product_status;
|
||||
$this->pui_helper = $pui_helper;
|
||||
$this->capture_factory = $capture_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes PUI integration.
|
||||
*
|
||||
* @throws NotFoundException When setting is not found.
|
||||
*/
|
||||
public function init(): void {
|
||||
add_filter(
|
||||
'ppcp_partner_referrals_data',
|
||||
function ( array $data ): array {
|
||||
if ( $this->settings->has( 'ppcp-onboarding-pui' ) && $this->settings->get( 'ppcp-onboarding-pui' ) !== '1' ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data['business_entity'] = array(
|
||||
'business_type' => array(
|
||||
'type' => 'PRIVATE_CORPORATION',
|
||||
),
|
||||
'addresses' => array(
|
||||
array(
|
||||
'address_line_1' => WC()->countries->get_base_address(),
|
||||
'admin_area_1' => WC()->countries->get_base_city(),
|
||||
'postal_code' => WC()->countries->get_base_postcode(),
|
||||
'country_code' => WC()->countries->get_base_country(),
|
||||
'type' => 'WORK',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if ( in_array( 'PPCP', $data['products'], true ) ) {
|
||||
$data['products'][] = 'PAYMENT_METHODS';
|
||||
} elseif ( in_array( 'EXPRESS_CHECKOUT', $data['products'], true ) ) {
|
||||
$data['products'][0] = 'PAYMENT_METHODS';
|
||||
}
|
||||
$data['capabilities'][] = 'PAY_UPON_INVOICE';
|
||||
|
||||
return $data;
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
array( $this, 'register_assets' )
|
||||
);
|
||||
|
||||
add_action(
|
||||
'ppcp_payment_capture_completed_webhook_handler',
|
||||
function ( WC_Order $wc_order, string $order_id ) {
|
||||
try {
|
||||
$order = $this->pui_order_endpoint->order( $order_id );
|
||||
|
||||
$payment_instructions = array(
|
||||
$order->payment_source->pay_upon_invoice->payment_reference,
|
||||
$order->payment_source->pay_upon_invoice->deposit_bank_details,
|
||||
);
|
||||
$wc_order->update_meta_data(
|
||||
'ppcp_ratepay_payment_instructions_payment_reference',
|
||||
$payment_instructions
|
||||
);
|
||||
$wc_order->save_meta_data();
|
||||
$this->logger->info( "Ratepay payment instructions added to order #{$wc_order->get_id()}." );
|
||||
|
||||
$capture = $this->capture_factory->from_paypal_response( $order->purchase_units[0]->payments->captures[0] );
|
||||
$breakdown = $capture->seller_receivable_breakdown();
|
||||
if ( $breakdown ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::FEES_META_KEY, $breakdown->to_array() );
|
||||
$wc_order->save_meta_data();
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error( $exception->getMessage() );
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_email_before_order_table',
|
||||
function( WC_Order $order, bool $sent_to_admin ) {
|
||||
if ( ! $sent_to_admin && PayUponInvoiceGateway::ID === $order->get_payment_method() && $order->has_status( 'processing' ) ) {
|
||||
$this->logger->info( "Adding Ratepay payment instructions to email for order #{$order->get_id()}." );
|
||||
|
||||
$instructions = $order->get_meta( 'ppcp_ratepay_payment_instructions_payment_reference' );
|
||||
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$merchant_name = $gateway_settings['brand_name'] ?? '';
|
||||
|
||||
$order_date = $order->get_date_created();
|
||||
if ( null === $order_date ) {
|
||||
$this->logger->error( 'Could not get WC order date for Ratepay payment instructions.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$order_purchase_date = $order_date->date( 'd-m-Y' );
|
||||
$order_time = $order_date->date( 'H:i:s' );
|
||||
$order_date = $order_date->date( 'd-m-Y H:i:s' );
|
||||
|
||||
$thirty_days_date = strtotime( $order_date . ' +30 days' );
|
||||
if ( false === $thirty_days_date ) {
|
||||
$this->logger->error( 'Could not create +30 days date from WC order date.' );
|
||||
return;
|
||||
}
|
||||
$order_date_30d = gmdate( 'd-m-Y', $thirty_days_date );
|
||||
|
||||
$payment_reference = $instructions[0] ?? '';
|
||||
$bic = $instructions[1]->bic ?? '';
|
||||
$bank_name = $instructions[1]->bank_name ?? '';
|
||||
$iban = $instructions[1]->iban ?? '';
|
||||
$account_holder_name = $instructions[1]->account_holder_name ?? '';
|
||||
|
||||
echo wp_kses_post( "<p>Für Ihre Bestellung #{$order->get_id()} ({$order_purchase_date} $order_time) bei {$merchant_name} haben Sie die Zahlung mittels “Rechnungskauf mit Ratepay“ gewählt." );
|
||||
echo '<br>Bitte benutzen Sie die folgenden Informationen für Ihre Überweisung:</br>';
|
||||
echo wp_kses_post( "<p>Bitte überweisen Sie den Betrag in Höhe von {$order->get_currency()}{$order->get_total()} bis zum {$order_date_30d} auf das unten angegebene Konto. Wichtig: Bitte geben Sie unbedingt als Verwendungszweck {$payment_reference} an, sonst kann die Zahlung nicht zugeordnet werden.</p>" );
|
||||
|
||||
echo '<ul>';
|
||||
echo wp_kses_post( "<li>Empfänger: {$account_holder_name}</li>" );
|
||||
echo wp_kses_post( "<li>IBAN: {$iban}</li>" );
|
||||
echo wp_kses_post( "<li>BIC: {$bic}</li>" );
|
||||
echo wp_kses_post( "<li>Name der Bank: {$bank_name}</li>" );
|
||||
echo wp_kses_post( "<li>Verwendungszweck: {$payment_reference}</li>" );
|
||||
echo '</ul>';
|
||||
|
||||
echo wp_kses_post( "<p>{$merchant_name} hat die Forderung gegen Sie an die PayPal (Europe) S.à r.l. et Cie, S.C.A. abgetreten, die wiederum die Forderung an Ratepay GmbH abgetreten hat. Zahlungen mit schuldbefreiender Wirkung können nur an die Ratepay GmbH geleistet werden.</p>" );
|
||||
|
||||
echo '<p>Mit freundlichen Grüßen';
|
||||
echo '<br>';
|
||||
echo wp_kses_post( "{$merchant_name}</p>" );
|
||||
}
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_gateway_description',
|
||||
function( string $description, string $id ): string {
|
||||
if ( PayUponInvoiceGateway::ID === $id ) {
|
||||
ob_start();
|
||||
|
||||
$site_country_code = explode( '-', get_bloginfo( 'language' ) )[0] ?? '';
|
||||
|
||||
echo '<div style="padding: 20px 0;">';
|
||||
|
||||
woocommerce_form_field(
|
||||
'billing_birth_date',
|
||||
array(
|
||||
'type' => 'date',
|
||||
'label' => 'de' === $site_country_code ? 'Geburtsdatum' : 'Birth date',
|
||||
'class' => array( 'form-row-wide' ),
|
||||
'required' => true,
|
||||
'clear' => true,
|
||||
)
|
||||
);
|
||||
|
||||
echo '</div><div>';
|
||||
|
||||
// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
|
||||
$button_text = apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) );
|
||||
|
||||
if ( 'de' === $site_country_code ) {
|
||||
echo wp_kses_post(
|
||||
'Mit Klicken auf ' . $button_text . ' akzeptieren Sie die <a href="https://www.ratepay.com/legal-payment-terms" target="_blank">Ratepay Zahlungsbedingungen</a> und erklären sich mit der Durchführung einer <a href="https://www.ratepay.com/legal-payment-dataprivacy" target="_blank">Risikoprüfung durch Ratepay</a>, unseren Partner, einverstanden. Sie akzeptieren auch PayPals <a href="https://www.paypal.com/de/webapps/mpp/ua/privacy-full?locale.x=de_DE&_ga=1.228729434.718583817.1563460395" target="_blank">Datenschutzerklärung</a>. Falls Ihre Transaktion per Kauf auf Rechnung erfolgreich abgewickelt werden kann, wird der Kaufpreis an Ratepay abgetreten und Sie dürfen nur an Ratepay überweisen, nicht an den Händler.'
|
||||
);
|
||||
} else {
|
||||
echo wp_kses_post(
|
||||
'By clicking on ' . $button_text . ', you agree to the <a href="https://www.ratepay.com/legal-payment-terms" target="_blank">terms of payment</a> and <a href="https://www.ratepay.com/legal-payment-dataprivacy">performance of a risk check</a> from the payment partner, Ratepay. You also agree to PayPal’s <a href="https://www.paypal.com/de/webapps/mpp/ua/privacy-full?locale.x=eng_DE&_ga=1.267010504.718583817.1563460395">privacy statement</a>. If your request to purchase upon invoice is accepted, the purchase price claim will be assigned to Ratepay, and you may only pay Ratepay, not the merchant.'
|
||||
);
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
$description .= ob_get_clean() ?: '';
|
||||
}
|
||||
|
||||
return $description;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_after_checkout_validation',
|
||||
function( array $fields, WP_Error $errors ) {
|
||||
$payment_method = filter_input( INPUT_POST, 'payment_method', FILTER_SANITIZE_STRING );
|
||||
if ( PayUponInvoiceGateway::ID !== $payment_method ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'DE' !== $fields['billing_country'] ) {
|
||||
$errors->add( 'validation', __( 'Billing country not available.', 'woocommerce-paypal-payments' ) );
|
||||
}
|
||||
|
||||
$birth_date = filter_input( INPUT_POST, 'billing_birth_date', FILTER_SANITIZE_STRING );
|
||||
if ( ( $birth_date && ! $this->pui_helper->validate_birth_date( $birth_date ) ) || $birth_date === '' ) {
|
||||
$errors->add( 'validation', __( 'Invalid birth date.', 'woocommerce-paypal-payments' ) );
|
||||
}
|
||||
|
||||
$national_number = filter_input( INPUT_POST, 'billing_phone', FILTER_SANITIZE_STRING );
|
||||
if ( $national_number ) {
|
||||
$numeric_phone_number = preg_replace( '/[^0-9]/', '', $national_number );
|
||||
if ( $numeric_phone_number && ! preg_match( '/^[0-9]{1,14}?$/', $numeric_phone_number ) ) {
|
||||
$errors->add( 'validation', __( 'Phone number size must be between 1 and 14', 'woocommerce-paypal-payments' ) );
|
||||
}
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_available_payment_gateways',
|
||||
function ( array $methods ): array {
|
||||
if ( State::STATE_ONBOARDED !== $this->state->current_state() ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
if (
|
||||
! $this->pui_product_status->pui_is_active()
|
||||
|| ! $this->pui_helper->is_checkout_ready_for_pui()
|
||||
) {
|
||||
unset( $methods[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_settings_checkout',
|
||||
function () {
|
||||
if (
|
||||
PayUponInvoiceGateway::ID === $this->current_ppcp_settings_page_id
|
||||
&& ! $this->pui_product_status->pui_is_active()
|
||||
) {
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$gateway_enabled = $gateway_settings['enabled'] ?? '';
|
||||
if ( 'yes' === $gateway_enabled ) {
|
||||
$gateway_settings['enabled'] = 'no';
|
||||
update_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings', $gateway_settings );
|
||||
$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' );
|
||||
wp_safe_redirect( $redirect_url );
|
||||
exit;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="notice notice-error"><p>%1$s</p></div>',
|
||||
esc_html__( 'Could not enable gateway because the connected PayPal account is not activated for Pay upon Invoice. Reconnect your account while Onboard with Pay upon Invoice is selected to try again.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_update_options_checkout_ppcp-pay-upon-invoice-gateway',
|
||||
function () {
|
||||
$customer_service_instructions = filter_input( INPUT_POST, 'woocommerce_ppcp-pay-upon-invoice-gateway_customer_service_instructions', FILTER_SANITIZE_STRING );
|
||||
if ( '' === $customer_service_instructions ) {
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$gateway_enabled = $gateway_settings['enabled'] ?? '';
|
||||
if ( 'yes' === $gateway_enabled ) {
|
||||
$gateway_settings['enabled'] = 'no';
|
||||
update_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings', $gateway_settings );
|
||||
|
||||
$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' );
|
||||
wp_safe_redirect( $redirect_url );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_settings_checkout',
|
||||
function() {
|
||||
if (
|
||||
PayUponInvoiceGateway::ID === $this->current_ppcp_settings_page_id
|
||||
&& $this->pui_product_status->pui_is_active()
|
||||
) {
|
||||
$error_messages = array();
|
||||
$pui_gateway = WC()->payment_gateways->payment_gateways()[ PayUponInvoiceGateway::ID ];
|
||||
if ( $pui_gateway->get_option( 'logo_url' ) === '' ) {
|
||||
$error_messages[] = esc_html__( 'Could not enable gateway because "Logo URL" field is empty.', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
if ( $pui_gateway->get_option( 'customer_service_instructions' ) === '' ) {
|
||||
$error_messages[] = esc_html__( 'Could not enable gateway because "Customer service instructions" field is empty.', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
if ( count( $error_messages ) > 0 ) { ?>
|
||||
<div class="notice notice-error">
|
||||
<?php
|
||||
array_map(
|
||||
static function( $message ) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p>' . $message . '</p>';
|
||||
},
|
||||
$error_messages
|
||||
)
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'add_meta_boxes',
|
||||
function( string $post_type ) {
|
||||
if ( $post_type === 'shop_order' ) {
|
||||
$post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_STRING );
|
||||
$order = wc_get_order( $post_id );
|
||||
if ( is_a( $order, WC_Order::class ) && $order->get_payment_method() === 'ppcp-pay-upon-invoice-gateway' ) {
|
||||
$instructions = $order->get_meta( 'ppcp_ratepay_payment_instructions_payment_reference' );
|
||||
if ( $instructions ) {
|
||||
add_meta_box(
|
||||
'ppcp_pui_ratepay_payment_instructions',
|
||||
__( 'RatePay payment instructions', 'woocommerce-paypal-payments' ),
|
||||
function() use ( $instructions ) {
|
||||
$payment_reference = $instructions[0] ?? '';
|
||||
$bic = $instructions[1]->bic ?? '';
|
||||
$bank_name = $instructions[1]->bank_name ?? '';
|
||||
$iban = $instructions[1]->iban ?? '';
|
||||
$account_holder_name = $instructions[1]->account_holder_name ?? '';
|
||||
|
||||
echo '<ul>';
|
||||
echo wp_kses_post( "<li>Empfänger: {$account_holder_name}</li>" );
|
||||
echo wp_kses_post( "<li>IBAN: {$iban}</li>" );
|
||||
echo wp_kses_post( "<li>BIC: {$bic}</li>" );
|
||||
echo wp_kses_post( "<li>Name der Bank: {$bank_name}</li>" );
|
||||
echo wp_kses_post( "<li>Verwendungszweck: {$payment_reference}</li>" );
|
||||
echo '</ul>';
|
||||
},
|
||||
$post_type,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers PUI assets.
|
||||
*/
|
||||
public function register_assets(): void {
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$gateway_enabled = $gateway_settings['enabled'] ?? '';
|
||||
if ( $gateway_enabled === 'yes' && ( is_checkout() || is_checkout_pay_page() ) ) {
|
||||
wp_enqueue_script(
|
||||
'ppcp-pay-upon-invoice',
|
||||
trailingslashit( $this->module_url ) . 'assets/js/pay-upon-invoice.js',
|
||||
array(),
|
||||
$this->asset_version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-pay-upon-invoice',
|
||||
'FraudNetConfig',
|
||||
array(
|
||||
'f' => $this->fraud_net->session_id(),
|
||||
's' => $this->fraud_net->source_website_id(),
|
||||
'sandbox' => $this->environment->current_environment_is( Environment::SANDBOX ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
<?php
|
||||
/**
|
||||
* The Pay upon invoice Gateway
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WC_Order_Item_Product;
|
||||
use WC_Payment_Gateway;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
|
||||
|
||||
/**
|
||||
* Class PayUponInvoiceGateway.
|
||||
*/
|
||||
class PayUponInvoiceGateway extends WC_Payment_Gateway {
|
||||
|
||||
use OrderMetaTrait;
|
||||
|
||||
const ID = 'ppcp-pay-upon-invoice-gateway';
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var PayUponInvoiceOrderEndpoint
|
||||
*/
|
||||
protected $order_endpoint;
|
||||
|
||||
/**
|
||||
* The purchase unit factory.
|
||||
*
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
protected $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The payment source factory.
|
||||
*
|
||||
* @var PaymentSourceFactory
|
||||
*/
|
||||
protected $payment_source_factory;
|
||||
|
||||
/**
|
||||
* The environment.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
protected $environment;
|
||||
|
||||
/**
|
||||
* The transaction url provider.
|
||||
*
|
||||
* @var TransactionUrlProvider
|
||||
*/
|
||||
protected $transaction_url_provider;
|
||||
|
||||
/**
|
||||
* The logger interface.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* The PUI helper.
|
||||
*
|
||||
* @var PayUponInvoiceHelper
|
||||
*/
|
||||
protected $pui_helper;
|
||||
|
||||
/**
|
||||
* PayUponInvoiceGateway constructor.
|
||||
*
|
||||
* @param PayUponInvoiceOrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param PaymentSourceFactory $payment_source_factory The payment source factory.
|
||||
* @param Environment $environment The environment.
|
||||
* @param TransactionUrlProvider $transaction_url_provider The transaction URL provider.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param PayUponInvoiceHelper $pui_helper The PUI helper.
|
||||
*/
|
||||
public function __construct(
|
||||
PayUponInvoiceOrderEndpoint $order_endpoint,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
PaymentSourceFactory $payment_source_factory,
|
||||
Environment $environment,
|
||||
TransactionUrlProvider $transaction_url_provider,
|
||||
LoggerInterface $logger,
|
||||
PayUponInvoiceHelper $pui_helper
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
|
||||
$this->method_title = __( 'Pay upon Invoice', 'woocommerce-paypal-payments' );
|
||||
$this->method_description = __( 'Pay upon Invoice is an invoice payment method in Germany. It is a local buy now, pay later payment method that allows the buyer to place an order, receive the goods, try them, verify they are in good order, and then pay the invoice within 30 days.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$this->title = $gateway_settings['title'] ?? $this->method_title;
|
||||
$this->description = $gateway_settings['description'] ?? __( 'Once you place an order, pay within 30 days. Our payment partner Ratepay will send you payment instructions.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
||||
add_action(
|
||||
'woocommerce_update_options_payment_gateways_' . $this->id,
|
||||
array(
|
||||
$this,
|
||||
'process_admin_options',
|
||||
)
|
||||
);
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->payment_source_factory = $payment_source_factory;
|
||||
$this->logger = $logger;
|
||||
$this->environment = $environment;
|
||||
$this->transaction_url_provider = $transaction_url_provider;
|
||||
$this->pui_helper = $pui_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the form fields.
|
||||
*/
|
||||
public function init_form_fields() {
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ),
|
||||
'default' => 'no',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enable/Disable Pay upon Invoice payment gateway.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'title' => array(
|
||||
'title' => __( 'Title', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => $this->title,
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'description' => array(
|
||||
'title' => __( 'Description', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => $this->description,
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'experience_context' => array(
|
||||
'title' => __( 'Experience Context', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'title',
|
||||
'description' => __( "Specify brand name, logo and customer service instructions to be presented on Ratepay's payment instructions.", 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'brand_name' => array(
|
||||
'title' => __( 'Brand name', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => get_bloginfo( 'name' ) ?? '',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Merchant name displayed in Ratepay\'s payment instructions.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'logo_url' => array(
|
||||
'title' => __( 'Logo URL', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'url',
|
||||
'default' => '',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Logo to be presented on Ratepay\'s payment instructions.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'customer_service_instructions' => array(
|
||||
'title' => __( 'Customer service instructions', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Customer service instructions to be presented on Ratepay\'s payment instructions.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the order.
|
||||
*
|
||||
* @param int $order_id The WC order ID.
|
||||
* @return array
|
||||
*/
|
||||
public function process_payment( $order_id ) {
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
$birth_date = filter_input( INPUT_POST, 'billing_birth_date', FILTER_SANITIZE_STRING ) ?? '';
|
||||
|
||||
$pay_for_order = filter_input( INPUT_GET, 'pay_for_order', FILTER_SANITIZE_STRING );
|
||||
if ( 'true' === $pay_for_order ) {
|
||||
if ( ! $this->pui_helper->validate_birth_date( $birth_date ) ) {
|
||||
wc_add_notice( 'Invalid birth date.', 'error' );
|
||||
return array(
|
||||
'result' => 'failure',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$wc_order->update_status( 'on-hold', __( 'Awaiting Pay upon Invoice payment.', 'woocommerce-paypal-payments' ) );
|
||||
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
$payment_source = $this->payment_source_factory->from_wc_order( $wc_order, $birth_date );
|
||||
|
||||
try {
|
||||
$order = $this->order_endpoint->create( array( $purchase_unit ), $payment_source );
|
||||
$this->add_paypal_meta( $wc_order, $order, $this->environment );
|
||||
|
||||
as_schedule_single_action(
|
||||
time() + ( 5 * MINUTE_IN_SECONDS ),
|
||||
'woocommerce_paypal_payments_check_pui_payment_captured',
|
||||
array(
|
||||
'wc_order_id' => $order_id,
|
||||
'order_id' => $order->id(),
|
||||
)
|
||||
);
|
||||
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
return array(
|
||||
'result' => 'success',
|
||||
'redirect' => $this->get_return_url( $wc_order ),
|
||||
);
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
|
||||
if ( is_a( $exception, PayPalApiException::class ) && is_array( $exception->details() ) ) {
|
||||
$details = '';
|
||||
foreach ( $exception->details() as $detail ) {
|
||||
$issue = $detail->issue ?? '';
|
||||
$field = $detail->field ?? '';
|
||||
$description = $detail->description ?? '';
|
||||
$details .= $issue . ' ' . $field . ' ' . $description . '<br>';
|
||||
}
|
||||
|
||||
$error = $details;
|
||||
}
|
||||
|
||||
$this->logger->error( $error );
|
||||
wc_add_notice( $error, 'error' );
|
||||
|
||||
$wc_order->update_status(
|
||||
'failed',
|
||||
$error
|
||||
);
|
||||
|
||||
return array(
|
||||
'result' => 'failure',
|
||||
'redirect' => wc_get_checkout_url(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return transaction url for this gateway and given order.
|
||||
*
|
||||
* @param WC_Order $order WC order to get transaction url by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_transaction_url( $order ): string {
|
||||
$this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order );
|
||||
|
||||
return parent::get_transaction_url( $order );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
<?php
|
||||
/**
|
||||
* PUI payment source.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
/**
|
||||
* Class PaymentSource.
|
||||
*/
|
||||
class PaymentSource {
|
||||
|
||||
/**
|
||||
* The given name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $given_name;
|
||||
|
||||
/**
|
||||
* The surname.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $surname;
|
||||
|
||||
/**
|
||||
* The email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $email;
|
||||
|
||||
/**
|
||||
* The birth date.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $birth_date;
|
||||
|
||||
/**
|
||||
* The phone number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $national_number;
|
||||
|
||||
/**
|
||||
* The phone country code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $phone_country_code;
|
||||
|
||||
/**
|
||||
* The address line 1.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $address_line_1;
|
||||
|
||||
/**
|
||||
* The admin area 2.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $admin_area_2;
|
||||
|
||||
/**
|
||||
* The postal code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $postal_code;
|
||||
|
||||
/**
|
||||
* The country code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $country_code;
|
||||
|
||||
/**
|
||||
* The locale.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $locale;
|
||||
|
||||
/**
|
||||
* The brand name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $brand_name;
|
||||
|
||||
/**
|
||||
* The logo URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $logo_url;
|
||||
|
||||
/**
|
||||
* The customer service instructions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $customer_service_instructions;
|
||||
|
||||
/**
|
||||
* PaymentSource constructor.
|
||||
*
|
||||
* @param string $given_name The given name.
|
||||
* @param string $surname The surname.
|
||||
* @param string $email The email.
|
||||
* @param string $birth_date The birth date.
|
||||
* @param string $national_number The phone number.
|
||||
* @param string $phone_country_code The phone country code.
|
||||
* @param string $address_line_1 The address line 1.
|
||||
* @param string $admin_area_2 The admin area 2.
|
||||
* @param string $postal_code The postal code.
|
||||
* @param string $country_code The country code.
|
||||
* @param string $locale The locale.
|
||||
* @param string $brand_name The brand name.
|
||||
* @param string $logo_url The logo URL.
|
||||
* @param array $customer_service_instructions The customer service instructions.
|
||||
*/
|
||||
public function __construct(
|
||||
string $given_name,
|
||||
string $surname,
|
||||
string $email,
|
||||
string $birth_date,
|
||||
string $national_number,
|
||||
string $phone_country_code,
|
||||
string $address_line_1,
|
||||
string $admin_area_2,
|
||||
string $postal_code,
|
||||
string $country_code,
|
||||
string $locale,
|
||||
string $brand_name,
|
||||
string $logo_url,
|
||||
array $customer_service_instructions
|
||||
) {
|
||||
$this->given_name = $given_name;
|
||||
$this->surname = $surname;
|
||||
$this->email = $email;
|
||||
$this->birth_date = $birth_date;
|
||||
$this->national_number = $national_number;
|
||||
$this->phone_country_code = $phone_country_code;
|
||||
$this->address_line_1 = $address_line_1;
|
||||
$this->admin_area_2 = $admin_area_2;
|
||||
$this->postal_code = $postal_code;
|
||||
$this->country_code = $country_code;
|
||||
$this->locale = $locale;
|
||||
$this->brand_name = $brand_name;
|
||||
$this->logo_url = $logo_url;
|
||||
$this->customer_service_instructions = $customer_service_instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function given_name(): string {
|
||||
return $this->given_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the surname.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function surname(): string {
|
||||
return $this->surname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the email.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function email(): string {
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the birth date.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function birth_date(): string {
|
||||
return $this->birth_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the national number.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function national_number(): string {
|
||||
return $this->national_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the phone country code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function phone_country_code(): string {
|
||||
return $this->phone_country_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address line 1.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function address_line_1(): string {
|
||||
return $this->address_line_1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the admin area 2.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function admin_area_2(): string {
|
||||
return $this->admin_area_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the postal code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function postal_code(): string {
|
||||
return $this->postal_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the country code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function country_code(): string {
|
||||
return $this->country_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function locale(): string {
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the brand name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function brand_name(): string {
|
||||
return $this->brand_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The logo URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function logo_url(): string {
|
||||
return $this->logo_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the customer service instructions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function customer_service_instructions(): array {
|
||||
return $this->customer_service_instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns payment source as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'name' => array(
|
||||
'given_name' => $this->given_name(),
|
||||
'surname' => $this->surname(),
|
||||
),
|
||||
'email' => $this->email(),
|
||||
'birth_date' => $this->birth_date(),
|
||||
'phone' => array(
|
||||
'national_number' => $this->national_number(),
|
||||
'country_code' => $this->phone_country_code(),
|
||||
),
|
||||
'billing_address' => array(
|
||||
'address_line_1' => $this->address_line_1(),
|
||||
'admin_area_2' => $this->admin_area_2(),
|
||||
'postal_code' => $this->postal_code(),
|
||||
'country_code' => $this->country_code(),
|
||||
),
|
||||
'experience_context' => array(
|
||||
'locale' => $this->locale(),
|
||||
'brand_name' => $this->brand_name(),
|
||||
'logo_url' => $this->logo_url(),
|
||||
'customer_service_instructions' => $this->customer_service_instructions(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/**
|
||||
* PUI payment source factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
use WC_Order;
|
||||
|
||||
/**
|
||||
* Class PaymentSourceFactory.
|
||||
*/
|
||||
class PaymentSourceFactory {
|
||||
|
||||
/**
|
||||
* Create a PUI payment source from a WC order.
|
||||
*
|
||||
* @param WC_Order $order The WC order.
|
||||
* @param string $birth_date The birth date.
|
||||
* @return PaymentSource
|
||||
*/
|
||||
public function from_wc_order( WC_Order $order, string $birth_date ) {
|
||||
$address = $order->get_address();
|
||||
|
||||
$phone_country_code = WC()->countries->get_country_calling_code( $address['country'] );
|
||||
$phone_country_code = is_array( $phone_country_code ) && ! empty( $phone_country_code ) ? $phone_country_code[0] : $phone_country_code;
|
||||
if ( is_string( $phone_country_code ) && '' !== $phone_country_code ) {
|
||||
$phone_country_code = substr( $phone_country_code, strlen( '+' ) ) ?: '';
|
||||
} else {
|
||||
$phone_country_code = '';
|
||||
}
|
||||
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$merchant_name = $gateway_settings['brand_name'] ?? '';
|
||||
$logo_url = $gateway_settings['logo_url'] ?? '';
|
||||
$customer_service_instructions = $gateway_settings['customer_service_instructions'] ?? '';
|
||||
|
||||
return new PaymentSource(
|
||||
$address['first_name'] ?? '',
|
||||
$address['last_name'] ?? '',
|
||||
$address['email'] ?? '',
|
||||
$birth_date,
|
||||
preg_replace( '/[^0-9]/', '', $address['phone'] ) ?? '',
|
||||
$phone_country_code,
|
||||
$address['address_1'] ?? '',
|
||||
$address['city'] ?? '',
|
||||
$address['postcode'] ?? '',
|
||||
$address['country'] ?? '',
|
||||
'en-DE',
|
||||
$merchant_name,
|
||||
$logo_url,
|
||||
array( $customer_service_instructions )
|
||||
);
|
||||
}
|
||||
}
|
|
@ -82,9 +82,16 @@ trait ProcessPaymentTrait {
|
|||
|
||||
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
$payer = $this->payer_factory->from_customer( $customer );
|
||||
|
||||
$shipping_preference = $this->shipping_preference_factory->from_state(
|
||||
$purchase_unit,
|
||||
''
|
||||
);
|
||||
|
||||
try {
|
||||
$order = $this->order_endpoint->create(
|
||||
array( $purchase_unit ),
|
||||
$shipping_preference,
|
||||
$payer,
|
||||
$selected_token
|
||||
);
|
||||
|
|
140
modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php
Normal file
140
modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
/**
|
||||
* Helper methods for PUI.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Helper
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
|
||||
|
||||
use DateTime;
|
||||
use WC_Order;
|
||||
use WC_Order_Item_Product;
|
||||
use WC_Product;
|
||||
use WC_Product_Variable;
|
||||
use WC_Product_Variation;
|
||||
|
||||
/**
|
||||
* Class PayUponInvoiceHelper
|
||||
*/
|
||||
class PayUponInvoiceHelper {
|
||||
|
||||
/**
|
||||
* Ensures date is valid and at least 18 years back.
|
||||
*
|
||||
* @param string $date The date.
|
||||
* @param string $format The date format.
|
||||
* @return bool
|
||||
*/
|
||||
public function validate_birth_date( string $date, string $format = 'Y-m-d' ): bool {
|
||||
$d = DateTime::createFromFormat( $format, $date );
|
||||
if ( false === $d ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $date !== $d->format( $format ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$date_time = strtotime( $date );
|
||||
if ( $date_time && time() < strtotime( '+18 years', $date_time ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures product is ready for PUI.
|
||||
*
|
||||
* @param WC_Product $product WC product.
|
||||
* @return bool
|
||||
*/
|
||||
public function product_ready_for_pui( WC_Product $product ):bool {
|
||||
if ( $product->is_downloadable() || $product->is_virtual() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_a( $product, WC_Product_Variable::class ) ) {
|
||||
foreach ( $product->get_available_variations( 'object' ) as $variation ) {
|
||||
if ( is_a( $variation, WC_Product_Variation::class ) ) {
|
||||
if ( true === $variation->is_downloadable() || true === $variation->is_virtual() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether checkout is ready for PUI.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_checkout_ready_for_pui(): bool {
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
if ( $gateway_settings && '' === $gateway_settings['customer_service_instructions'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$billing_country = filter_input( INPUT_POST, 'country', FILTER_SANITIZE_STRING ) ?? null;
|
||||
if ( $billing_country && 'DE' !== $billing_country ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'EUR' !== get_woocommerce_currency() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cart = WC()->cart ?? null;
|
||||
if ( $cart && ! is_checkout_pay_page() ) {
|
||||
$cart_total = (float) $cart->get_total( 'numeric' );
|
||||
if ( $cart_total < 5 || $cart_total > 2500 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$items = $cart->get_cart_contents();
|
||||
foreach ( $items as $item ) {
|
||||
$product = wc_get_product( $item['product_id'] );
|
||||
if ( is_a( $product, WC_Product::class ) && ! $this->product_ready_for_pui( $product ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_wc_endpoint_url( 'order-pay' ) ) {
|
||||
/**
|
||||
* Needed for WordPress `query_vars`.
|
||||
*
|
||||
* @psalm-suppress InvalidGlobal
|
||||
*/
|
||||
global $wp;
|
||||
|
||||
if ( isset( $wp->query_vars['order-pay'] ) && absint( $wp->query_vars['order-pay'] ) > 0 ) {
|
||||
$order_id = absint( $wp->query_vars['order-pay'] );
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( is_a( $order, WC_Order::class ) ) {
|
||||
$order_total = (float) $order->get_total();
|
||||
if ( $order_total < 5 || $order_total > 2500 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $order->get_items() as $item_id => $item ) {
|
||||
if ( is_a( $item, WC_Order_Item_Product::class ) ) {
|
||||
$product = wc_get_product( $item->get_product_id() );
|
||||
if ( is_a( $product, WC_Product::class ) && ! $this->product_ready_for_pui( $product ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
/**
|
||||
* Manage the Seller status.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Helper
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusProduct;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class PayUponInvoiceProductStatus
|
||||
*/
|
||||
class PayUponInvoiceProductStatus {
|
||||
|
||||
/**
|
||||
* Caches the status for the current load.
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
private $current_status_cache;
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* The partners endpoint.
|
||||
*
|
||||
* @var PartnersEndpoint
|
||||
*/
|
||||
private $partners_endpoint;
|
||||
|
||||
/**
|
||||
* PayUponInvoiceProductStatus constructor.
|
||||
*
|
||||
* @param Settings $settings The Settings.
|
||||
* @param PartnersEndpoint $partners_endpoint The Partner Endpoint.
|
||||
*/
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
PartnersEndpoint $partners_endpoint
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->partners_endpoint = $partners_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the active/subscribed products support PUI.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function pui_is_active() : bool {
|
||||
if ( is_bool( $this->current_status_cache ) ) {
|
||||
return $this->current_status_cache;
|
||||
}
|
||||
if ( $this->settings->has( 'products_pui_enabled' ) && $this->settings->get( 'products_pui_enabled' ) ) {
|
||||
$this->current_status_cache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$seller_status = $this->partners_endpoint->seller_status();
|
||||
} catch ( RuntimeException $error ) {
|
||||
$this->current_status_cache = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $seller_status->products() as $product ) {
|
||||
if ( $product->name() !== 'PAYMENT_METHODS' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! in_array(
|
||||
$product->vetting_status(),
|
||||
array(
|
||||
SellerStatusProduct::VETTING_STATUS_APPROVED,
|
||||
SellerStatusProduct::VETTING_STATUS_SUBSCRIBED,
|
||||
),
|
||||
true
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( in_array( 'PAY_UPON_INVOICE', $product->capabilities(), true ) ) {
|
||||
$this->settings->set( 'products_pui_enabled', true );
|
||||
$this->settings->persist();
|
||||
$this->current_status_cache = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->current_status_cache = false;
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -41,6 +41,8 @@ trait OrderMetaTrait {
|
|||
if ( $payment_source ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY, $payment_source );
|
||||
}
|
||||
|
||||
$wc_order->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
|
|||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage;
|
||||
|
||||
/**
|
||||
|
@ -27,13 +28,22 @@ class SectionsRenderer {
|
|||
*/
|
||||
protected $page_id;
|
||||
|
||||
/**
|
||||
* The api shop country.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $api_shop_country;
|
||||
|
||||
/**
|
||||
* SectionsRenderer constructor.
|
||||
*
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param string $api_shop_country The api shop country.
|
||||
*/
|
||||
public function __construct( string $page_id ) {
|
||||
public function __construct( string $page_id, string $api_shop_country ) {
|
||||
$this->page_id = $page_id;
|
||||
$this->api_shop_country = $api_shop_country;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,15 +66,23 @@ class SectionsRenderer {
|
|||
$sections = array(
|
||||
PayPalGateway::ID => __( 'PayPal Checkout', 'woocommerce-paypal-payments' ),
|
||||
CreditCardGateway::ID => __( 'PayPal Card Processing', 'woocommerce-paypal-payments' ),
|
||||
PayUponInvoiceGateway::ID => __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ),
|
||||
WebhooksStatusPage::ID => __( 'Webhooks Status', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
|
||||
if ( 'DE' !== $this->api_shop_country ) {
|
||||
unset( $sections[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
||||
echo '<ul class="subsubsub">';
|
||||
|
||||
$array_keys = array_keys( $sections );
|
||||
|
||||
foreach ( $sections as $id => $label ) {
|
||||
$url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&' . self::KEY . '=' . $id );
|
||||
if ( PayUponInvoiceGateway::ID === $id ) {
|
||||
$url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' );
|
||||
}
|
||||
echo '<li><a href="' . esc_url( $url ) . '" class="' . ( $this->page_id === $id ? 'current' : '' ) . '">' . esc_html( $label ) . '</a> ' . ( end( $array_keys ) === $id ? '' : '|' ) . ' </li>';
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,20 @@ class SettingsListener {
|
|||
*/
|
||||
protected $page_id;
|
||||
|
||||
/**
|
||||
* The signup link cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
protected $signup_link_cache;
|
||||
|
||||
/**
|
||||
* Signup link ids
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $signup_link_ids;
|
||||
|
||||
/**
|
||||
* SettingsListener constructor.
|
||||
*
|
||||
|
@ -91,6 +105,8 @@ class SettingsListener {
|
|||
* @param State $state The state.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param Cache $signup_link_cache The signup link cache.
|
||||
* @param array $signup_link_ids Signup link ids.
|
||||
*/
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
|
@ -99,7 +115,9 @@ class SettingsListener {
|
|||
Cache $cache,
|
||||
State $state,
|
||||
Bearer $bearer,
|
||||
string $page_id
|
||||
string $page_id,
|
||||
Cache $signup_link_cache,
|
||||
array $signup_link_ids
|
||||
) {
|
||||
|
||||
$this->settings = $settings;
|
||||
|
@ -109,6 +127,8 @@ class SettingsListener {
|
|||
$this->state = $state;
|
||||
$this->bearer = $bearer;
|
||||
$this->page_id = $page_id;
|
||||
$this->signup_link_cache = $signup_link_cache;
|
||||
$this->signup_link_ids = $signup_link_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,6 +271,7 @@ class SettingsListener {
|
|||
if ( $credentials_change_status ) {
|
||||
if ( self::CREDENTIALS_UNCHANGED !== $credentials_change_status ) {
|
||||
$this->settings->set( 'products_dcc_enabled', null );
|
||||
$this->settings->set( 'products_pui_enabled', null );
|
||||
}
|
||||
|
||||
if ( in_array(
|
||||
|
@ -259,6 +280,12 @@ class SettingsListener {
|
|||
true
|
||||
) ) {
|
||||
$this->webhook_registrar->unregister();
|
||||
|
||||
foreach ( $this->signup_link_ids as $key ) {
|
||||
if ( $this->signup_link_cache->has( $key ) ) {
|
||||
$this->signup_link_cache->delete( $key );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use Dhii\Modular\Module\ModuleInterface;
|
|||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
|
||||
|
@ -214,7 +215,7 @@ class WCGatewayModule implements ModuleInterface {
|
|||
assert( $settings instanceof Settings );
|
||||
|
||||
try {
|
||||
if ( $settings->get( '3d_secure_contingency' ) === '3D_SECURE' ) {
|
||||
if ( $settings->has( '3d_secure_contingency' ) && $settings->get( '3d_secure_contingency' ) === '3D_SECURE' ) {
|
||||
$settings->set( '3d_secure_contingency', 'SCA_ALWAYS' );
|
||||
$settings->persist();
|
||||
}
|
||||
|
@ -223,6 +224,42 @@ class WCGatewayModule implements ModuleInterface {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
function () use ( $c ) {
|
||||
if ( 'DE' === $c->get( 'api.shop.country' ) && 'EUR' === $c->get( 'api.shop.currency' ) ) {
|
||||
( $c->get( 'wcgateway.pay-upon-invoice' ) )->init();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_check_pui_payment_captured',
|
||||
function ( int $wc_order_id, string $order_id ) use ( $c ) {
|
||||
$order_endpoint = $c->get( 'api.endpoint.order' );
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
$order = $order_endpoint->order( $order_id );
|
||||
$order_status = $order->status();
|
||||
$logger->info( "Checking payment captured webhook for WC order #{$wc_order_id}, PayPal order status: " . $order_status->name() );
|
||||
|
||||
$wc_order = wc_get_order( $wc_order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) || $wc_order->get_status() !== 'on-hold' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $order_status->name() !== OrderStatus::COMPLETED ) {
|
||||
$message = __(
|
||||
'Could not process WC order because PAYMENT.CAPTURE.COMPLETED webhook not received.',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
$logger->error( $message );
|
||||
$wc_order->update_status( 'failed', $message );
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -246,6 +283,11 @@ class WCGatewayModule implements ModuleInterface {
|
|||
if ( $dcc_applies->for_country_currency() ) {
|
||||
$methods[] = $container->get( 'wcgateway.credit-card-gateway' );
|
||||
}
|
||||
|
||||
if ( 'DE' === $container->get( 'api.shop.country' ) && 'EUR' === $container->get( 'api.shop.currency' ) ) {
|
||||
$methods[] = $container->get( 'wcgateway.pay-upon-invoice-gateway' );
|
||||
}
|
||||
|
||||
return (array) $methods;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = {
|
|||
target: 'web',
|
||||
entry: {
|
||||
'gateway-settings': path.resolve('./resources/js/gateway-settings.js'),
|
||||
'pay-upon-invoice': path.resolve('./resources/js/pay-upon-invoice.js'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'assets/'),
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
|
||||
/**
|
||||
* Class CheckoutOrderApproved
|
||||
|
@ -188,6 +189,10 @@ class CheckoutOrderApproved implements RequestHandler {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
|
||||
/**
|
||||
* Class CheckoutOrderCompleted
|
||||
|
@ -131,6 +132,10 @@ class CheckoutOrderCompleted implements RequestHandler {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -112,6 +112,13 @@ class PaymentCaptureCompleted implements RequestHandler {
|
|||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$order_id = $resource['supplementary_data']['related_ids']['order_id'] ?? null;
|
||||
|
||||
/**
|
||||
* Allow access to the webhook logic before updating the WC order.
|
||||
*/
|
||||
do_action( 'ppcp_payment_capture_completed_webhook_handler', $wc_order, $order_id );
|
||||
|
||||
if ( $wc_order->get_status() !== 'on-hold' ) {
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
|
@ -139,8 +146,6 @@ class PaymentCaptureCompleted implements RequestHandler {
|
|||
)
|
||||
);
|
||||
|
||||
$order_id = $resource['supplementary_data']['related_ids']['order_id'] ?? null;
|
||||
|
||||
if ( $order_id ) {
|
||||
try {
|
||||
$order = $this->order_endpoint->order( (string) $order_id );
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "woocommerce-paypal-payments",
|
||||
"version": "1.8.2",
|
||||
"version": "1.9.1",
|
||||
"description": "WooCommerce PayPal Payments",
|
||||
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
|
||||
"license": "GPL-2.0",
|
||||
|
|
|
@ -23,6 +23,12 @@
|
|||
<exclude name="WordPress.Files.FileName.NotHyphenatedLowercase" />
|
||||
<exclude name="WordPress.Files.FileName.InvalidClassFileName" />
|
||||
<exclude name="WordPress.PHP.DisallowShortTernary" />
|
||||
<exclude name="WordPress.PHP.YodaConditions.NotYoda" />
|
||||
</rule>
|
||||
|
||||
<rule ref="WooCommerce">
|
||||
<exclude name="WooCommerce.Commenting.CommentHooks.MissingHookComment" />
|
||||
<exclude name="WooCommerce.Commenting.CommentHooks.MissingSinceComment" />
|
||||
</rule>
|
||||
|
||||
<rule ref="WooCommerce">
|
||||
|
|
16
readme.txt
16
readme.txt
|
@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, e-commerce, store, sales, sell,
|
|||
Requires at least: 5.3
|
||||
Tested up to: 6.0
|
||||
Requires PHP: 7.1
|
||||
Stable tag: 1.8.2
|
||||
Stable tag: 1.9.1
|
||||
License: GPLv2
|
||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
|
@ -81,7 +81,19 @@ Follow the steps below to connect the plugin to your PayPal account:
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 1.8.2 =
|
||||
= 1.9.1 =
|
||||
* Fix - ITEM_TOTAL_MISMATCH error when checking out with multiple products #721
|
||||
* Fix - Unable to purchase a product with Credit card button in pay for order page #718
|
||||
* Fix - Pay Later messaging only displayed when smart button is active on the same page #283
|
||||
* Fix - Pay Later messaging displayed for out of stock variable products or with no variation selected #667
|
||||
* Fix - Placeholders and card type detection not working for PayPal Card Processing (260) #685
|
||||
* Fix - PUI gateway is displayed with unsupported store currency #711
|
||||
* Enhancement - Missing PayPal fee in WC order details for PUI purchase #714
|
||||
* Enhancement - Skip loading of PUI js file on all pages where PUI gateway is not displayed #723
|
||||
* Enhancement - PUI feature capitalization not consistent #724
|
||||
|
||||
= 1.9.0 =
|
||||
* Add - New Feature - Pay Upon Invoice (Germany only) #608
|
||||
* Fix - Order not approved: payment via vaulted PayPal account fails #677
|
||||
* Fix - Cant' refund : "ERROR Refund failed: No country given for address." #639
|
||||
* Fix - Something went wrong error in Virtual products when using vaulted payment #673
|
||||
|
|
|
@ -952,7 +952,7 @@ class OrderEndpointTest extends TestCase
|
|||
->expects('email_address')
|
||||
->andReturn('');
|
||||
|
||||
$result = $testee->create([$purchaseUnit], $payer);
|
||||
$result = $testee->create([$purchaseUnit], ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING, $payer);
|
||||
$this->assertEquals($expectedOrder, $result);
|
||||
}
|
||||
|
||||
|
@ -1049,7 +1049,7 @@ class OrderEndpointTest extends TestCase
|
|||
$payerName = Mockery::mock(PayerName::class);
|
||||
$payer->expects('name')->andReturn($payerName);
|
||||
$payer->expects('to_array')->andReturn(['payer']);
|
||||
$result = $testee->create([$purchaseUnit], $payer);
|
||||
$result = $testee->create([$purchaseUnit], ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE, $payer);
|
||||
$this->assertEquals($expectedOrder, $result);
|
||||
}
|
||||
|
||||
|
@ -1141,7 +1141,7 @@ class OrderEndpointTest extends TestCase
|
|||
$payerName = Mockery::mock(PayerName::class);
|
||||
$payer->expects('name')->andReturn($payerName);
|
||||
$payer->expects('to_array')->andReturn(['payer']);
|
||||
$testee->create([$purchaseUnit], $payer);
|
||||
$testee->create([$purchaseUnit], ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING, $payer);
|
||||
}
|
||||
|
||||
public function testCreateForPurchaseUnitsIsNot201()
|
||||
|
@ -1232,6 +1232,6 @@ class OrderEndpointTest extends TestCase
|
|||
$payerName = Mockery::mock(PayerName::class);
|
||||
$payer->expects('name')->andReturn($payerName);
|
||||
$payer->expects('to_array')->andReturn(['payer']);
|
||||
$testee->create([$purchaseUnit], $payer);
|
||||
$testee->create([$purchaseUnit], ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE, $payer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Mockery;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Requests_Utility_CaseInsensitiveDictionary;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Token;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSource;
|
||||
use function Brain\Monkey\Functions\expect;
|
||||
use function Brain\Monkey\Functions\when;
|
||||
|
||||
class PayUponInvoiceOrderEndpointTest extends TestCase
|
||||
{
|
||||
private $bearer;
|
||||
private $orderFactory;
|
||||
private $fraudnet;
|
||||
private $logger;
|
||||
private $testee;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->bearer = Mockery::mock(Bearer::class);
|
||||
$token = Mockery::mock(Token::class);
|
||||
$token->shouldReceive('token')->andReturn('');
|
||||
$this->bearer->shouldReceive('bearer')->andReturn($token);
|
||||
|
||||
$this->orderFactory = Mockery::mock(OrderFactory::class);
|
||||
$this->fraudnet = Mockery::mock(FraudNet::class);
|
||||
$this->logger = Mockery::mock(LoggerInterface::class);
|
||||
|
||||
$this->testee = new PayUponInvoiceOrderEndpoint(
|
||||
'',
|
||||
$this->bearer,
|
||||
$this->orderFactory,
|
||||
$this->fraudnet,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateOrder()
|
||||
{
|
||||
list($items, $paymentSource, $headers) = $this->setStubs();
|
||||
|
||||
$response = [
|
||||
'body' => '{"is_correct":true}',
|
||||
'headers' => $headers,
|
||||
];
|
||||
expect('wp_remote_get')->andReturn($response);
|
||||
expect('wp_remote_retrieve_response_code')->with($response)->andReturn(200);
|
||||
|
||||
$this->logger->shouldReceive('debug');
|
||||
|
||||
$result = $this->testee->create($items, $paymentSource, '');
|
||||
$this->assertInstanceOf(Order::class, $result);
|
||||
}
|
||||
|
||||
public function testCreateOrderWpError()
|
||||
{
|
||||
list($items, $paymentSource) = $this->setStubsForError();
|
||||
|
||||
$wpError = Mockery::mock(\WP_Error::class);
|
||||
$wpError->shouldReceive('get_error_messages')->andReturn(['foo']);
|
||||
$wpError->shouldReceive('get_error_message')->andReturn('foo');
|
||||
expect('wp_remote_get')->andReturn($wpError);
|
||||
|
||||
$this->logger->shouldReceive('debug');
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->testee->create($items, $paymentSource, '');
|
||||
}
|
||||
|
||||
public function testCreateOrderApiError()
|
||||
{
|
||||
list($items, $paymentSource) = $this->setStubsForError();
|
||||
|
||||
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
|
||||
$headers->shouldReceive('getAll');
|
||||
$response = [
|
||||
'body' => '{"is_correct":true}',
|
||||
'headers' => $headers,
|
||||
];
|
||||
|
||||
when('get_bloginfo')->justReturn('de-DE');
|
||||
expect('wp_remote_get')->andReturn($response);
|
||||
expect('wp_remote_retrieve_response_code')->with($response)->andReturn(500);
|
||||
|
||||
$this->logger->shouldReceive('debug');
|
||||
|
||||
$this->expectException(PayPalApiException::class);
|
||||
$this->testee->create($items, $paymentSource, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function setStubs(): array
|
||||
{
|
||||
$order = Mockery::mock(Order::class);
|
||||
$this->orderFactory
|
||||
->expects('from_paypal_response')
|
||||
->andReturnUsing(function (\stdClass $object) use ($order): ?Order {
|
||||
return ($object->is_correct) ? $order : null;
|
||||
});
|
||||
|
||||
$this->fraudnet->shouldReceive('session_id')->andReturn('');
|
||||
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit->shouldReceive('to_array')->andReturn([
|
||||
'items' => [],
|
||||
]);
|
||||
$items = [$purchaseUnit];
|
||||
|
||||
$paymentSource = Mockery::mock(PaymentSource::class);
|
||||
$paymentSource->shouldReceive('to_array')->andReturn([]);
|
||||
|
||||
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
|
||||
$headers->shouldReceive('getAll');
|
||||
return array($items, $paymentSource, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function setStubsForError(): array
|
||||
{
|
||||
$this->fraudnet->shouldReceive('session_id')->andReturn('');
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit->shouldReceive('to_array')->andReturn([
|
||||
'items' => [],
|
||||
]);
|
||||
$items = [$purchaseUnit];
|
||||
$paymentSource = Mockery::mock(PaymentSource::class);
|
||||
$paymentSource->shouldReceive('to_array')->andReturn([]);
|
||||
return array($items, $paymentSource);
|
||||
}
|
||||
}
|
|
@ -22,9 +22,7 @@ class AmountTest extends TestCase
|
|||
|
||||
public function testBreakdownIsNull()
|
||||
{
|
||||
$money = Mockery::mock(Money::class);
|
||||
$money->shouldReceive('currency_code')->andReturn('currencyCode');
|
||||
$money->shouldReceive('value')->andReturn(1.10);
|
||||
$money = new Money(1.10, 'currencyCode');
|
||||
$testee = new Amount($money);
|
||||
|
||||
$this->assertNull($testee->breakdown());
|
||||
|
@ -38,9 +36,7 @@ class AmountTest extends TestCase
|
|||
|
||||
public function testBreakdown()
|
||||
{
|
||||
$money = Mockery::mock(Money::class);
|
||||
$money->shouldReceive('currency_code')->andReturn('currencyCode');
|
||||
$money->shouldReceive('value')->andReturn(1.10);
|
||||
$money = new Money(1.10, 'currencyCode');
|
||||
$breakdown = Mockery::mock(AmountBreakdown::class);
|
||||
$breakdown->shouldReceive('to_array')->andReturn([1]);
|
||||
$testee = new Amount($money, $breakdown);
|
||||
|
|
|
@ -412,10 +412,8 @@ class PurchaseUnitTest extends TestCase
|
|||
foreach ($data as $testKey => $test) {
|
||||
$items = [];
|
||||
foreach ($test['items'] as $key => $item) {
|
||||
$unitAmount = Mockery::mock(Money::class);
|
||||
$unitAmount->shouldReceive('value')->andReturn($item['value']);
|
||||
$tax = Mockery::mock(Money::class);
|
||||
$tax->shouldReceive('value')->andReturn($item['tax']);
|
||||
$unitAmount = new Money($item['value'], 'EUR');
|
||||
$tax = new Money($item['tax'], 'EUR');
|
||||
$items[$key] = Mockery::mock(
|
||||
Item::class,
|
||||
[
|
||||
|
@ -436,15 +434,14 @@ class PurchaseUnitTest extends TestCase
|
|||
return null;
|
||||
}
|
||||
|
||||
$money = Mockery::mock(Money::class);
|
||||
$money->shouldReceive('value')->andReturn($value);
|
||||
$money = new Money($value, 'EUR');
|
||||
return $money;
|
||||
});
|
||||
}
|
||||
}
|
||||
$amount = Mockery::mock(Amount::class);
|
||||
$amount->shouldReceive('to_array')->andReturn(['value' => number_format( $test['amount'], 2, '.', '' ), 'breakdown' => []]);
|
||||
$amount->shouldReceive('value')->andReturn($test['amount']);
|
||||
$amount->shouldReceive('value_str')->andReturn(number_format( $test['amount'], 2, '.', '' ));
|
||||
$amount->shouldReceive('currency_code')->andReturn('EUR');
|
||||
$amount->shouldReceive('breakdown')->andReturn($breakdown);
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ class ItemFactoryTest extends TestCase
|
|||
$product
|
||||
->expects('is_virtual')
|
||||
->andReturn(true);
|
||||
|
||||
$items = [
|
||||
[
|
||||
'data' => $product,
|
||||
|
@ -108,6 +109,7 @@ class ItemFactoryTest extends TestCase
|
|||
$woocommerce->session = $session;
|
||||
$session->shouldReceive('get')->andReturn([]);
|
||||
|
||||
|
||||
$result = $testee->from_wc_cart($cart);
|
||||
|
||||
$item = current($result);
|
||||
|
@ -158,6 +160,7 @@ class ItemFactoryTest extends TestCase
|
|||
->expects('get_fees')
|
||||
->andReturn([]);
|
||||
|
||||
|
||||
$result = $testee->from_wc_order($order);
|
||||
$this->assertCount(1, $result);
|
||||
$item = current($result);
|
||||
|
@ -186,6 +189,7 @@ class ItemFactoryTest extends TestCase
|
|||
$product
|
||||
->expects('is_virtual')
|
||||
->andReturn(true);
|
||||
|
||||
expect('wp_strip_all_tags')
|
||||
->with('description')
|
||||
->andReturn('description');
|
||||
|
@ -240,6 +244,7 @@ class ItemFactoryTest extends TestCase
|
|||
$product
|
||||
->expects('is_virtual')
|
||||
->andReturn(true);
|
||||
|
||||
expect('wp_strip_all_tags')
|
||||
->with($description)
|
||||
->andReturn(mb_substr( $description, 0, 127 ));
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use Mockery;
|
||||
use WC_Cart;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
|
||||
class ShippingPreferenceFactoryTest extends TestCase
|
||||
{
|
||||
private $testee;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->testee = new ShippingPreferenceFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider forStateData
|
||||
*/
|
||||
public function testFromState(
|
||||
PurchaseUnit $purchase_unit,
|
||||
string $context,
|
||||
?WC_Cart $cart,
|
||||
string $funding_source,
|
||||
string $expected_result
|
||||
) {
|
||||
$result = $this->testee->from_state($purchase_unit, $context, $cart, $funding_source);
|
||||
|
||||
self::assertEquals($expected_result, $result);
|
||||
}
|
||||
|
||||
public function forStateData()
|
||||
{
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, Mockery::mock(Shipping::class)),
|
||||
'checkout',
|
||||
$this->createCart(true),
|
||||
'',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(false, Mockery::mock(Shipping::class)),
|
||||
'checkout',
|
||||
$this->createCart(false),
|
||||
'',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, null),
|
||||
'checkout',
|
||||
$this->createCart(true),
|
||||
'',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, Mockery::mock(Shipping::class)),
|
||||
'checkout',
|
||||
$this->createCart(true),
|
||||
'card',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, null),
|
||||
'product',
|
||||
null,
|
||||
'',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, null),
|
||||
'pay-now',
|
||||
null,
|
||||
'venmo',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, Mockery::mock(Shipping::class)),
|
||||
'pay-now',
|
||||
null,
|
||||
'venmo',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, Mockery::mock(Shipping::class)),
|
||||
'pay-now',
|
||||
null,
|
||||
'card',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS,
|
||||
];
|
||||
yield [
|
||||
$this->createPurchaseUnit(true, null),
|
||||
'pay-now',
|
||||
null,
|
||||
'card',
|
||||
ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING,
|
||||
];
|
||||
}
|
||||
|
||||
private function createPurchaseUnit(bool $containsPhysicalGoods, ?Shipping $shipping): PurchaseUnit {
|
||||
$pu = Mockery::mock(PurchaseUnit::class);
|
||||
$pu->shouldReceive('contains_physical_goods')->andReturn($containsPhysicalGoods);
|
||||
$pu->shouldReceive('shipping')->andReturn($shipping);
|
||||
return $pu;
|
||||
}
|
||||
|
||||
private function createCart(bool $needsShipping): WC_Cart {
|
||||
$cart = Mockery::mock(WC_Cart::class);
|
||||
$cart->shouldReceive('needs_shipping')->andReturn($needsShipping);
|
||||
return $cart;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
|||
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use Mockery;
|
||||
use WooCommerce\WooCommerce\Logging\Logger\NullLogger;
|
||||
|
@ -17,7 +17,7 @@ class ChangeCartEndpointTest extends TestCase
|
|||
/**
|
||||
* @dataProvider dataForTestProducts
|
||||
*/
|
||||
public function testProducts($data, $products, $lineItems, $responseExpectation) {
|
||||
public function testProducts($data, $products, $responseExpectation) {
|
||||
|
||||
$dataStore = Mockery::mock(\WC_Data_Store::class);
|
||||
$cart = Mockery::mock(\WC_Cart::class);
|
||||
|
@ -59,16 +59,21 @@ class ChangeCartEndpointTest extends TestCase
|
|||
->expects('read_request')
|
||||
->with(ChangeCartEndpoint::nonce())
|
||||
->andReturn($data);
|
||||
$cartRepository = Mockery::mock(CartRepository::class);
|
||||
$cartRepository
|
||||
->expects('all')
|
||||
->andReturn($lineItems);
|
||||
|
||||
$pu = Mockery::mock(PurchaseUnit::class);
|
||||
$pu
|
||||
->shouldReceive('to_array')
|
||||
->andReturn($responseExpectation[0]);
|
||||
$purchase_unit_factory = Mockery::mock(PurchaseUnitFactory::class);
|
||||
$purchase_unit_factory
|
||||
->expects('from_wc_cart')
|
||||
->andReturn($pu);
|
||||
|
||||
$testee = new ChangeCartEndpoint(
|
||||
$cart,
|
||||
$shipping,
|
||||
$requestData,
|
||||
$cartRepository,
|
||||
$purchase_unit_factory,
|
||||
$dataStore,
|
||||
new NullLogger()
|
||||
);
|
||||
|
@ -97,15 +102,6 @@ class ChangeCartEndpointTest extends TestCase
|
|||
->with('variable')
|
||||
->andReturn(true);
|
||||
|
||||
$defaultLineItem = Mockery::mock(PurchaseUnit::class);
|
||||
$defaultLineItem
|
||||
->shouldReceive('to_array')
|
||||
->andReturn([1]);
|
||||
$variationLineItem = Mockery::mock(PurchaseUnit::class);
|
||||
$variationLineItem
|
||||
->shouldReceive('to_array')
|
||||
->andReturn([2]);
|
||||
|
||||
$testData = [
|
||||
'default' => [
|
||||
[
|
||||
|
@ -120,9 +116,6 @@ class ChangeCartEndpointTest extends TestCase
|
|||
[
|
||||
$defaultProduct,
|
||||
],
|
||||
[
|
||||
$defaultLineItem,
|
||||
],
|
||||
[
|
||||
[1],
|
||||
]
|
||||
|
@ -162,11 +155,7 @@ class ChangeCartEndpointTest extends TestCase
|
|||
$variationProduct,
|
||||
],
|
||||
[
|
||||
$defaultLineItem,
|
||||
$variationLineItem,
|
||||
],
|
||||
[
|
||||
[1],[2]
|
||||
[1, 2]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
|
@ -10,7 +10,7 @@ use ReflectionClass;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
|
@ -145,7 +145,7 @@ class CreateOrderEndpointTest extends TestCase
|
|||
protected function mockTestee()
|
||||
{
|
||||
$request_data = Mockery::mock(RequestData::class);
|
||||
$cart_repository = Mockery::mock(CartRepository::class);
|
||||
$shippingPreferenceFactory = Mockery::mock(ShippingPreferenceFactory::class);
|
||||
$purchase_unit_factory = Mockery::mock(PurchaseUnitFactory::class);
|
||||
$order_endpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$payer_factory = Mockery::mock(PayerFactory::class);
|
||||
|
@ -155,8 +155,8 @@ class CreateOrderEndpointTest extends TestCase
|
|||
|
||||
$testee = new CreateOrderEndpoint(
|
||||
$request_data,
|
||||
$cart_repository,
|
||||
$purchase_unit_factory,
|
||||
$shippingPreferenceFactory,
|
||||
$order_endpoint,
|
||||
$payer_factory,
|
||||
$session_handler,
|
||||
|
|
|
@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use Mockery;
|
||||
|
@ -31,6 +32,7 @@ class RenewalHandlerTest extends TestCase
|
|||
private $repository;
|
||||
private $orderEndpoint;
|
||||
private $purchaseUnitFactory;
|
||||
private $shippingPreferenceFactory;
|
||||
private $payerFactory;
|
||||
private $environment;
|
||||
private $sut;
|
||||
|
@ -43,6 +45,7 @@ class RenewalHandlerTest extends TestCase
|
|||
$this->repository = Mockery::mock(PaymentTokenRepository::class);
|
||||
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$this->purchaseUnitFactory = Mockery::mock(PurchaseUnitFactory::class);
|
||||
$this->shippingPreferenceFactory = Mockery::mock(ShippingPreferenceFactory::class);
|
||||
$this->payerFactory = Mockery::mock(PayerFactory::class);
|
||||
$this->environment = new Environment(new Dictionary([]));
|
||||
|
||||
|
@ -56,6 +59,7 @@ class RenewalHandlerTest extends TestCase
|
|||
$this->repository,
|
||||
$this->orderEndpoint,
|
||||
$this->purchaseUnitFactory,
|
||||
$this->shippingPreferenceFactory,
|
||||
$this->payerFactory,
|
||||
$this->environment
|
||||
);
|
||||
|
@ -133,11 +137,16 @@ class RenewalHandlerTest extends TestCase
|
|||
$this->payerFactory->shouldReceive('from_customer')
|
||||
->andReturn($payer);
|
||||
|
||||
$this->shippingPreferenceFactory->shouldReceive('from_state')
|
||||
->with($purchaseUnit, 'renewal')
|
||||
->andReturn('no_shipping');
|
||||
|
||||
$this->orderEndpoint->shouldReceive('create')
|
||||
->with([$purchaseUnit], $payer, $token)
|
||||
->with([$purchaseUnit], 'no_shipping', $payer, $token)
|
||||
->andReturn($order);
|
||||
|
||||
$wcOrder->shouldReceive('update_status');
|
||||
$wcOrder->shouldReceive('save');
|
||||
|
||||
$this->sut->renew($wcOrder);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
|
||||
|
||||
use Mockery;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
|
||||
use function Brain\Monkey\Functions\when;
|
||||
|
||||
class PayUponInvoiceGatewayTest extends TestCase
|
||||
{
|
||||
private $order_endpoint;
|
||||
private $purchase_unit_factory;
|
||||
private $payment_source_factory;
|
||||
private $environment;
|
||||
private $transaction_url_provider;
|
||||
private $logger;
|
||||
private $testee;
|
||||
private $pui_helper;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->order_endpoint = Mockery::mock(PayUponInvoiceOrderEndpoint::class);
|
||||
$this->purchase_unit_factory = Mockery::mock(PurchaseUnitFactory::class);
|
||||
$this->payment_source_factory = Mockery::mock(PaymentSourceFactory::class);
|
||||
$this->environment = Mockery::mock(Environment::class);
|
||||
$this->logger = Mockery::mock(LoggerInterface::class);
|
||||
$this->transaction_url_provider = Mockery::mock(TransactionUrlProvider::class);
|
||||
$this->pui_helper = Mockery::mock(PayUponInvoiceHelper::class);
|
||||
|
||||
$this->setInitStubs();
|
||||
|
||||
$this->testee = new PayUponInvoiceGateway(
|
||||
$this->order_endpoint,
|
||||
$this->purchase_unit_factory,
|
||||
$this->payment_source_factory,
|
||||
$this->environment,
|
||||
$this->transaction_url_provider,
|
||||
$this->logger,
|
||||
$this->pui_helper
|
||||
);
|
||||
}
|
||||
|
||||
public function testProcessPayment()
|
||||
{
|
||||
list($order, $purchase_unit, $payment_source) = $this->setTestStubs();
|
||||
|
||||
$this->order_endpoint->shouldReceive('create')->with(
|
||||
[$purchase_unit],
|
||||
$payment_source
|
||||
)->andReturn($order);
|
||||
|
||||
define( 'MINUTE_IN_SECONDS', 60 );
|
||||
when('as_schedule_single_action')->justReturn();
|
||||
|
||||
$result = $this->testee->process_payment(1);
|
||||
$this->assertEquals('success', $result['result']);
|
||||
}
|
||||
|
||||
public function testProcessPaymentError()
|
||||
{
|
||||
list($order, $purchase_unit, $payment_source) = $this->setTestStubs();
|
||||
|
||||
$this->logger->shouldReceive('error');
|
||||
when('wc_add_notice')->justReturn();
|
||||
when('wc_get_checkout_url')->justReturn();
|
||||
|
||||
$this->order_endpoint->shouldReceive('create')->with(
|
||||
[$purchase_unit],
|
||||
$payment_source,
|
||||
''
|
||||
)->andThrows(\RuntimeException::class);
|
||||
|
||||
$result = $this->testee->process_payment(1);
|
||||
$this->assertEquals('failure', $result['result']);
|
||||
}
|
||||
|
||||
private function setInitStubs(): void
|
||||
{
|
||||
when('get_option')->justReturn([
|
||||
'title' => 'foo',
|
||||
'description' => 'bar',
|
||||
]);
|
||||
when('get_bloginfo')->justReturn('Foo');
|
||||
|
||||
$woocommerce = Mockery::mock(\WooCommerce::class);
|
||||
$cart = Mockery::mock(\WC_Cart::class);
|
||||
when('WC')->justReturn($woocommerce);
|
||||
$woocommerce->cart = $cart;
|
||||
$cart->shouldReceive('empty_cart');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function setTestStubs(): array
|
||||
{
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->shouldReceive('update_meta_data');
|
||||
$wcOrder->shouldReceive('update_status');
|
||||
$wcOrder->shouldReceive('save');
|
||||
when('wc_get_order')->justReturn($wcOrder);
|
||||
|
||||
$order = Mockery::mock(Order::class);
|
||||
$order->shouldReceive('id')->andReturn('1');
|
||||
$order->shouldReceive('intent')->andReturn('CAPTURE');
|
||||
$order->shouldReceive('payment_source')->andReturn('');
|
||||
|
||||
$purchase_unit = Mockery::mock(PurchaseUnit::class);
|
||||
$payment_source = Mockery::mock(PaymentSource::class);
|
||||
|
||||
$this->payment_source_factory->shouldReceive('from_wc_order')
|
||||
->with($wcOrder, '')
|
||||
->andReturn($payment_source);
|
||||
|
||||
$this->purchase_unit_factory->shouldReceive('from_wc_order')
|
||||
->with($wcOrder)
|
||||
->andReturn($purchase_unit);
|
||||
|
||||
$this->environment->shouldReceive('current_environment_is')->andReturn(true);
|
||||
|
||||
return array($order, $purchase_unit, $payment_source);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
|||
use Psr\Log\NullLogger;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
|
@ -44,9 +45,11 @@ class WcGatewayTest extends TestCase
|
|||
private $subscriptionHelper;
|
||||
private $environment;
|
||||
private $paymentTokenRepository;
|
||||
private $shipping_preference_factory;
|
||||
private $logger;
|
||||
private $paymentsEndpoint;
|
||||
private $orderEndpoint;
|
||||
private $apiShopCountry;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
@ -66,10 +69,12 @@ class WcGatewayTest extends TestCase
|
|||
$this->subscriptionHelper = Mockery::mock(SubscriptionHelper::class);
|
||||
$this->environment = Mockery::mock(Environment::class);
|
||||
$this->paymentTokenRepository = Mockery::mock(PaymentTokenRepository::class);
|
||||
$this->shipping_preference_factory = Mockery::mock(ShippingPreferenceFactory::class);
|
||||
$this->logger = Mockery::mock(LoggerInterface::class);
|
||||
$this->paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$this->funding_source_renderer = new FundingSourceRenderer($this->settings);
|
||||
$this->apiShopCountry = 'DE';
|
||||
|
||||
$this->onboardingState->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED);
|
||||
|
||||
|
@ -100,9 +105,11 @@ class WcGatewayTest extends TestCase
|
|||
PayPalGateway::ID,
|
||||
$this->environment,
|
||||
$this->paymentTokenRepository,
|
||||
$this->shipping_preference_factory,
|
||||
$this->logger,
|
||||
$this->paymentsEndpoint,
|
||||
$this->orderEndpoint
|
||||
$this->orderEndpoint,
|
||||
$this->apiShopCountry
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -135,7 +142,6 @@ class WcGatewayTest extends TestCase
|
|||
->with($orderId)
|
||||
->andReturn($wcOrder);
|
||||
|
||||
|
||||
when('wc_get_checkout_url')
|
||||
->justReturn('test');
|
||||
|
||||
|
|
32
tests/PHPUnit/WcGateway/Helper/PayUponInvoiceHelperTest.php
Normal file
32
tests/PHPUnit/WcGateway/Helper/PayUponInvoiceHelperTest.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
|
||||
|
||||
use DateTime;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
|
||||
class PayUponInvoiceHelperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider datesProvider
|
||||
*/
|
||||
public function testValidateBirthDate($input, $output)
|
||||
{
|
||||
$this->assertSame((new PayUponInvoiceHelper())->validate_birth_date($input), $output);
|
||||
}
|
||||
|
||||
public function datesProvider(): array{
|
||||
$format = 'Y-m-d';
|
||||
|
||||
return [
|
||||
['', false],
|
||||
[(new DateTime())->format($format), false],
|
||||
[(new DateTime('-17 years'))->format($format), false],
|
||||
['1942-02-31', false],
|
||||
['01-01-1942', false],
|
||||
['1942-01-01', true],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -168,6 +168,7 @@ class OrderProcessorTest extends TestCase
|
|||
->with('on-hold', 'Awaiting payment.');
|
||||
$wcOrder->expects('set_transaction_id')
|
||||
->with($transactionId);
|
||||
$wcOrder->shouldReceive('save');
|
||||
|
||||
$order_helper->shouldReceive('contains_physical_goods')->andReturn(true);
|
||||
|
||||
|
@ -286,6 +287,7 @@ class OrderProcessorTest extends TestCase
|
|||
->with($transactionId);
|
||||
$wcOrder
|
||||
->expects('payment_complete');
|
||||
$wcOrder->shouldReceive('save');
|
||||
|
||||
$order_helper->shouldReceive('contains_physical_goods')->andReturn(true);
|
||||
|
||||
|
@ -386,6 +388,7 @@ class OrderProcessorTest extends TestCase
|
|||
PayPalGateway::INTENT_META_KEY,
|
||||
$orderIntent
|
||||
);
|
||||
$wcOrder->shouldReceive('save');
|
||||
|
||||
$order_helper->shouldReceive('contains_physical_goods')->andReturn(true);
|
||||
|
||||
|
|
|
@ -32,12 +32,12 @@ class SettingsListenerTest extends ModularTestCase
|
|||
$webhook_registrar = Mockery::mock(WebhookRegistrar::class);
|
||||
$webhook_registrar->shouldReceive('unregister')->andReturnTrue();
|
||||
$webhook_registrar->shouldReceive('register')->andReturnTrue();
|
||||
|
||||
$cache = Mockery::mock(Cache::class);
|
||||
|
||||
$state = Mockery::mock(State::class);
|
||||
$state->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED);
|
||||
$bearer = Mockery::mock(Bearer::class);
|
||||
$signup_link_cache = Mockery::mock(Cache::class);
|
||||
$signup_link_ids = array();
|
||||
|
||||
$testee = new SettingsListener(
|
||||
$settings,
|
||||
|
@ -46,7 +46,9 @@ class SettingsListenerTest extends ModularTestCase
|
|||
$cache,
|
||||
$state,
|
||||
$bearer,
|
||||
PayPalGateway::ID
|
||||
PayPalGateway::ID,
|
||||
$signup_link_cache,
|
||||
$signup_link_ids
|
||||
);
|
||||
|
||||
$_GET['section'] = PayPalGateway::ID;
|
||||
|
@ -74,6 +76,8 @@ class SettingsListenerTest extends ModularTestCase
|
|||
$settings->shouldReceive('persist');
|
||||
$cache->shouldReceive('has')
|
||||
->andReturn(false);
|
||||
$signup_link_cache->shouldReceive('has')
|
||||
->andReturn(false);
|
||||
|
||||
$testee->listen();
|
||||
}
|
||||
|
|
|
@ -68,8 +68,6 @@ class PurchaseUnitTest extends TestCase
|
|||
$pu = $this->puFactory->from_wc_order($wcOrder);
|
||||
$puData = $pu->to_array();
|
||||
|
||||
self::assertTrue(isset($puData['amount']['breakdown']));
|
||||
|
||||
self::assertEquals($expectedAmount, $puData['amount']);
|
||||
}
|
||||
|
||||
|
@ -83,8 +81,6 @@ class PurchaseUnitTest extends TestCase
|
|||
$pu = $this->puFactory->from_wc_cart($this->cart);
|
||||
$puData = $pu->to_array();
|
||||
|
||||
self::assertTrue(isset($puData['amount']['breakdown']));
|
||||
|
||||
self::assertEquals($expectedAmount, $puData['amount']);
|
||||
}
|
||||
|
||||
|
@ -334,6 +330,37 @@ class PurchaseUnitTest extends TestCase
|
|||
],
|
||||
]),
|
||||
];
|
||||
|
||||
yield 'no decimals currency' => [
|
||||
[
|
||||
'currency' => 'JPY',
|
||||
'items' => [
|
||||
['price' => 18.0, 'quantity' => 2],
|
||||
],
|
||||
'shipping' => ['total' => 5.0],
|
||||
'billing' => ['city' => 'city2'],
|
||||
],
|
||||
self::adaptAmountFormat([
|
||||
'value' => 66,
|
||||
'breakdown' => [
|
||||
'item_total' => 36,
|
||||
'tax_total' => 25, // 24.60
|
||||
'shipping' => 5,
|
||||
],
|
||||
], 'JPY'),
|
||||
];
|
||||
|
||||
yield [
|
||||
[
|
||||
'items' => [
|
||||
['price' => 5.345, 'quantity' => 2],
|
||||
],
|
||||
'billing' => ['city' => 'city0'],
|
||||
],
|
||||
self::adaptAmountFormat([
|
||||
'value' => 10.69,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
public function cartData() {
|
||||
|
@ -390,6 +417,18 @@ class PurchaseUnitTest extends TestCase
|
|||
],
|
||||
], get_woocommerce_currency()),
|
||||
];
|
||||
|
||||
yield [
|
||||
[
|
||||
'products' => [
|
||||
['price' => 5.345, 'quantity' => 2],
|
||||
],
|
||||
'billing' => ['city' => 'city0'],
|
||||
],
|
||||
self::adaptAmountFormat([
|
||||
'value' => 10.69,
|
||||
], get_woocommerce_currency()),
|
||||
];
|
||||
}
|
||||
|
||||
private static function adaptAmountFormat(array $data, string $currency = null): array {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Plugin Name: WooCommerce PayPal Payments
|
||||
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
|
||||
* 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.
|
||||
* Version: 1.8.2
|
||||
* Version: 1.9.1
|
||||
* Author: WooCommerce
|
||||
* Author URI: https://woocommerce.com/
|
||||
* License: GPL-2.0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue