Introduce oxxo payment (WIP)

This commit is contained in:
dinamiko 2022-07-05 11:30:20 +02:00
parent 49048c59d5
commit 2b67884900
10 changed files with 344 additions and 104 deletions

View file

@ -30,6 +30,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNetSessionId;
@ -38,6 +40,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSourceFac
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\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
@ -2155,6 +2158,9 @@ return array(
$container->get( 'wcgateway.settings' )
);
},
'wcgateway.checkout-helper' => static function ( ContainerInterface $container ): CheckoutHelper {
return new CheckoutHelper();
},
'wcgateway.pay-upon-invoice-order-endpoint' => static function ( ContainerInterface $container ): PayUponInvoiceOrderEndpoint {
return new PayUponInvoiceOrderEndpoint(
$container->get( 'api.host' ),
@ -2175,7 +2181,8 @@ return array(
$container->get( 'onboarding.environment' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'wcgateway.pay-upon-invoice-helper' )
$container->get( 'wcgateway.pay-upon-invoice-helper' ),
$container->get( 'wcgateway.checkout-helper' )
);
},
'wcgateway.pay-upon-invoice-fraudnet-session-id' => static function ( ContainerInterface $container ): FraudNetSessionId {
@ -2193,7 +2200,9 @@ return array(
);
},
'wcgateway.pay-upon-invoice-helper' => static function( ContainerInterface $container ): PayUponInvoiceHelper {
return new PayUponInvoiceHelper();
return new PayUponInvoiceHelper(
$container->get( 'wcgateway.checkout-helper' )
);
},
'wcgateway.pay-upon-invoice-product-status' => static function( ContainerInterface $container ): PayUponInvoiceProductStatus {
return new PayUponInvoiceProductStatus(
@ -2214,9 +2223,18 @@ return array(
$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( 'wcgateway.pay-upon-invoice-helper' ),
$container->get( 'wcgateway.checkout-helper' )
);
},
'wcgateway.oxxo' => static function( ContainerInterface $container ): OXXO {
return new OXXO(
$container->get( 'wcgateway.checkout-helper' )
);
},
'wcgateway.oxxo-gateway' => static function( ContainerInterface $container ): OXXOGateway {
return new OXXOGateway();
},
'wcgateway.logging.is-enabled' => function ( ContainerInterface $container ) : bool {
$settings = $container->get( 'wcgateway.settings' );

View file

@ -0,0 +1,75 @@
<?php
/**
* OXXO integration.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
/**
* Class OXXO.
*/
class OXXO {
/**
* The checkout helper.
*
* @var CheckoutHelper
*/
protected $checkout_helper;
/**
* OXXO constructor.
*
* @param CheckoutHelper $checkout_helper The checkout helper.
*/
public function __construct( CheckoutHelper $checkout_helper ) {
$this->checkout_helper = $checkout_helper;
}
/**
* Initializes OXXO integration.
*/
public function init() {
add_filter(
'woocommerce_available_payment_gateways',
function ( array $methods ): array {
if ( ! $this->checkout_allowed_for_oxxo() ) {
unset( $methods[ OXXOGateway::ID ] );
}
return $methods;
}
);
}
/**
* Checks if checkout is allowed for OXXO.
*
* @return bool
*/
private function checkout_allowed_for_oxxo(): bool {
if ( 'MXN' !== get_woocommerce_currency() ) {
return false;
}
$billing_country = filter_input( INPUT_POST, 'country', FILTER_SANITIZE_STRING ) ?? null;
if ( $billing_country && 'MX' !== $billing_country ) {
return false;
}
if ( ! $this->checkout_helper->is_checkout_amount_allowed( 0, 10000 ) ) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* The OXXO Gateway
*
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO;
use WC_Payment_Gateway;
/**
* Class PayUponInvoiceGateway.
*/
class OXXOGateway extends WC_Payment_Gateway {
const ID = 'ppcp-oxxo-gateway';
/**
* OXXOGateway constructor.
*/
public function __construct() {
$this->id = self::ID;
$this->method_title = __( 'OXXO', 'woocommerce-paypal-payments' );
$this->method_description = __( 'OXXO is a Mexican chain of convenience stores.', 'woocommerce-paypal-payments' );
$gateway_settings = get_option( 'woocommerce_ppcp-oxxo-gateway_settings' );
$this->title = $gateway_settings['title'] ?? $this->method_title;
$this->description = $gateway_settings['description'] ?? __( 'OXXO allows you to pay bills and online purchases in-store with cash.', 'woocommerce-paypal-payments' );
$this->init_form_fields();
$this->init_settings();
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array(
$this,
'process_admin_options',
)
);
}
/**
* Initialize the form fields.
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'label' => __( 'OXXO', 'woocommerce-paypal-payments' ),
'default' => 'no',
'desc_tip' => true,
'description' => __( 'Enable/Disable OXXO 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' ),
),
);
}
}

View file

@ -11,14 +11,10 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
use Psr\Log\LoggerInterface;
use WC_Order;
use WC_Order_Item;
use WC_Order_Item_Product;
use WC_Product;
use WC_Product_Variable;
use WC_Product_Variation;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
@ -118,6 +114,13 @@ class PayUponInvoice {
*/
protected $pui_product_status;
/**
* The checkout helper.
*
* @var CheckoutHelper
*/
protected $checkout_helper;
/**
* PayUponInvoice constructor.
*
@ -133,6 +136,7 @@ class PayUponInvoice {
* @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 CheckoutHelper $checkout_helper The checkout helper.
*/
public function __construct(
string $module_url,
@ -146,7 +150,8 @@ class PayUponInvoice {
bool $is_ppcp_settings_page,
string $current_ppcp_settings_page_id,
PayUponInvoiceProductStatus $pui_product_status,
PayUponInvoiceHelper $pui_helper
PayUponInvoiceHelper $pui_helper,
CheckoutHelper $checkout_helper
) {
$this->module_url = $module_url;
$this->fraud_net = $fraud_net;
@ -160,6 +165,7 @@ class PayUponInvoice {
$this->current_ppcp_settings_page_id = $current_ppcp_settings_page_id;
$this->pui_product_status = $pui_product_status;
$this->pui_helper = $pui_helper;
$this->checkout_helper = $checkout_helper;
}
/**
@ -342,7 +348,7 @@ class PayUponInvoice {
}
$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 === '' ) {
if ( ( $birth_date && ! $this->checkout_helper->validate_birth_date( $birth_date ) ) || $birth_date === '' ) {
$errors->add( 'validation', __( 'Invalid birth date.', 'woocommerce-paypal-payments' ) );
}

View file

@ -20,6 +20,7 @@ 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\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
@ -81,6 +82,13 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
*/
protected $pui_helper;
/**
* The checkout helper.
*
* @var CheckoutHelper
*/
protected $checkout_helper;
/**
* PayUponInvoiceGateway constructor.
*
@ -91,6 +99,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
* @param TransactionUrlProvider $transaction_url_provider The transaction URL provider.
* @param LoggerInterface $logger The logger.
* @param PayUponInvoiceHelper $pui_helper The PUI helper.
* @param CheckoutHelper $checkout_helper The checkout helper.
*/
public function __construct(
PayUponInvoiceOrderEndpoint $order_endpoint,
@ -99,7 +108,8 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
Environment $environment,
TransactionUrlProvider $transaction_url_provider,
LoggerInterface $logger,
PayUponInvoiceHelper $pui_helper
PayUponInvoiceHelper $pui_helper,
CheckoutHelper $checkout_helper
) {
$this->id = self::ID;
@ -128,6 +138,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
$this->environment = $environment;
$this->transaction_url_provider = $transaction_url_provider;
$this->pui_helper = $pui_helper;
$this->checkout_helper = $checkout_helper;
}
/**
@ -198,7 +209,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
$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 ) ) {
if ( ! $this->checkout_helper->validate_birth_date( $birth_date ) ) {
wc_add_notice( 'Invalid birth date.', 'error' );
return array(
'result' => 'failure',

View file

@ -0,0 +1,128 @@
<?php
/**
* The Checkout helper.
*
* @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;
/**
* CheckoutHelper class.
*/
class CheckoutHelper {
/**
* Checks if amount is allowed within the given range.
*
* @param float $minimum Minimum amount.
* @param float $maximum Maximum amount.
* @return bool
*/
public function is_checkout_amount_allowed( float $minimum, float $maximum ): bool {
$cart = WC()->cart ?? null;
if ( $cart && ! is_checkout_pay_page() ) {
$cart_total = (float) $cart->get_total( 'numeric' );
if ( $cart_total < $minimum || $cart_total > $maximum ) {
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->is_physical_product( $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 < $minimum || $order_total > $maximum ) {
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->is_physical_product( $product ) ) {
return false;
}
}
}
}
}
}
return true;
}
/**
* 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 is_physical_product( 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;
}
}

View file

@ -9,65 +9,25 @@ 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.
* The checkout helper.
*
* @param string $date The date.
* @param string $format The date format.
* @return bool
* @var CheckoutHelper
*/
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;
}
protected $checkout_helper;
/**
* Ensures product is ready for PUI.
* PayUponInvoiceHelper constructor.
*
* @param WC_Product $product WC product.
* @return bool
* @param CheckoutHelper $checkout_helper The checkout helper.
*/
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;
public function __construct( CheckoutHelper $checkout_helper ) {
$this->checkout_helper = $checkout_helper;
}
/**
@ -90,49 +50,8 @@ class PayUponInvoiceHelper {
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;
}
}
}
}
}
if ( ! $this->checkout_helper->is_checkout_amount_allowed( 5, 2500 ) ) {
return false;
}
return true;

View file

@ -231,6 +231,8 @@ class WCGatewayModule implements ModuleInterface {
if ( 'DE' === $c->get( 'api.shop.country' ) && 'EUR' === get_woocommerce_currency() ) {
( $c->get( 'wcgateway.pay-upon-invoice' ) )->init();
}
( $c->get( 'wcgateway.oxxo' ) )->init();
}
);
@ -288,6 +290,8 @@ class WCGatewayModule implements ModuleInterface {
$methods[] = $container->get( 'wcgateway.pay-upon-invoice-gateway' );
}
$methods[] = $container->get( 'wcgateway.oxxo-gateway' );
return (array) $methods;
}
);

View file

@ -13,6 +13,7 @@ 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\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
use function Brain\Monkey\Functions\when;
@ -26,6 +27,7 @@ class PayUponInvoiceGatewayTest extends TestCase
private $logger;
private $testee;
private $pui_helper;
private $checkout_helper;
public function setUp(): void
{
@ -38,6 +40,7 @@ class PayUponInvoiceGatewayTest extends TestCase
$this->logger = Mockery::mock(LoggerInterface::class);
$this->transaction_url_provider = Mockery::mock(TransactionUrlProvider::class);
$this->pui_helper = Mockery::mock(PayUponInvoiceHelper::class);
$this->checkout_helper = Mockery::mock(CheckoutHelper::class);
$this->setInitStubs();
@ -48,7 +51,8 @@ class PayUponInvoiceGatewayTest extends TestCase
$this->environment,
$this->transaction_url_provider,
$this->logger,
$this->pui_helper
$this->pui_helper,
$this->checkout_helper
);
}

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
use DateTime;
use Mockery;
use WooCommerce\PayPalCommerce\TestCase;
class PayUponInvoiceHelperTest extends TestCase
@ -13,7 +14,7 @@ class PayUponInvoiceHelperTest extends TestCase
*/
public function testValidateBirthDate($input, $output)
{
$this->assertSame((new PayUponInvoiceHelper())->validate_birth_date($input), $output);
$this->assertSame((new CheckoutHelper())->validate_birth_date($input), $output);
}
public function datesProvider(): array{