enable gateways in the account -> order -> pay-now

This commit is contained in:
David Remer 2020-09-30 14:24:31 +03:00
parent 324ff49496
commit 96fd3d6a2a
10 changed files with 302 additions and 58 deletions

View file

@ -43,7 +43,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository;
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WpOop\TransientCache\CachePoolFactory;
return array(
'api.host' => function( $container ) : string {

View file

@ -36,6 +36,42 @@ class PayerFactory {
$this->address_factory = $address_factory;
}
/**
* Returns a Payer entity from a WooCommerce order.
*
* @param \WC_Order $wc_order The WooCommerce order.
*
* @return Payer
*/
public function from_wc_order( \WC_Order $wc_order ): Payer {
$payer_id = '';
$birthdate = null;
$phone = null;
if ( $wc_order->get_billing_phone() ) {
// make sure the phone number contains only numbers and is max 14. chars long.
$national_number = $wc_order->get_billing_phone();
$national_number = preg_replace( '/[^0-9]/', '', $national_number );
$national_number = substr( $national_number, 0, 14 );
$phone = new PhoneWithType(
'HOME',
new Phone( $national_number )
);
}
return new Payer(
new PayerName(
$wc_order->get_billing_first_name(),
$wc_order->get_billing_last_name()
),
$wc_order->get_billing_email(),
$payer_id,
$this->address_factory->from_wc_order( $wc_order, 'billing' ),
$birthdate,
$phone
);
}
/**
* Returns a Payer object based off a WooCommerce customer.
*

View file

@ -2,6 +2,7 @@ import MiniCartBootstap from './modules/ContextBootstrap/MiniCartBootstap';
import SingleProductBootstap from './modules/ContextBootstrap/SingleProductBootstap';
import CartBootstrap from './modules/ContextBootstrap/CartBootstap';
import CheckoutBootstap from './modules/ContextBootstrap/CheckoutBootstap';
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
import Renderer from './modules/Renderer/Renderer';
import ErrorHandler from './modules/ErrorHandler';
import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer";
@ -52,6 +53,15 @@ const bootstrap = () => {
checkoutBootstap.init();
}
if (context === 'pay-now' ) {
const payNowBootstrap = new PayNowBootstrap(
PayPalCommerceGateway,
renderer,
messageRenderer
);
payNowBootstrap.init();
}
if (context !== 'checkout') {
messageRenderer.render();
}

View file

@ -16,7 +16,8 @@ class CheckoutActionHandler {
const errorHandler = this.errorHandler;
const formValues = jQuery('form.checkout').serialize();
const formSelector = this.config.context === 'checkout' ? 'form.checkout' : 'form#order_review';
const formValues = jQuery(formSelector).serialize();
return fetch(this.config.ajax.create_order.endpoint, {
method: 'POST',
@ -25,6 +26,7 @@ class CheckoutActionHandler {
payer,
bn_code:bnCode,
context:this.config.context,
order_id:this.config.order_id,
form:formValues
})
}).then(function (res) {
@ -38,7 +40,7 @@ class CheckoutActionHandler {
input.setAttribute('type', 'hidden');
input.setAttribute('name', 'ppcp-resume-order');
input.setAttribute('value', data.data.purchase_units[0].custom_id);
document.querySelector('form.checkout').append(input);
document.querySelector(formSelector).append(input);
return data.data.id;
});
}

View file

@ -0,0 +1,80 @@
import ErrorHandler from '../ErrorHandler';
import CheckoutActionHandler from '../ActionHandler/CheckoutActionHandler';
class PayNowBootstrap {
constructor(gateway, renderer, messages) {
this.gateway = gateway;
this.renderer = renderer;
this.messages = messages
}
init() {
this.render();
jQuery(document.body).on('updated_checkout', () => {
this.render();
});
jQuery(document.body).
on('updated_checkout payment_method_selected', () => {
this.switchBetweenPayPalandOrderButton();
});
this.switchBetweenPayPalandOrderButton();
}
shouldRender() {
if (document.querySelector(this.gateway.button.cancel_wrapper)) {
return false;
}
return document.querySelector(this.gateway.button.wrapper) !== null || document.querySelector(this.gateway.hosted_fields.wrapper) !== null;
}
render() {
if (!this.shouldRender()) {
return;
}
if (document.querySelector(this.gateway.hosted_fields.wrapper + '>div')) {
document.querySelector(this.gateway.hosted_fields.wrapper + '>div').setAttribute('style', '');
}
const actionHandler = new CheckoutActionHandler(
PayPalCommerceGateway,
new ErrorHandler(this.gateway.labels.error.generic),
);
this.renderer.render(
this.gateway.button.wrapper,
this.gateway.hosted_fields.wrapper,
actionHandler.configuration(),
);
}
switchBetweenPayPalandOrderButton() {
const currentPaymentMethod = jQuery(
'input[name="payment_method"]:checked').val();
if (currentPaymentMethod !== 'ppcp-gateway' && currentPaymentMethod !== 'ppcp-credit-card-gateway') {
this.renderer.hideButtons(this.gateway.button.wrapper);
this.renderer.hideButtons(this.gateway.messages.wrapper);
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
jQuery('#place_order').show();
}
else {
jQuery('#place_order').hide();
if (currentPaymentMethod === 'ppcp-gateway') {
this.renderer.showButtons(this.gateway.button.wrapper);
this.renderer.showButtons(this.gateway.messages.wrapper);
this.messages.render();
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
}
if (currentPaymentMethod === 'ppcp-credit-card-gateway') {
this.renderer.hideButtons(this.gateway.button.wrapper);
this.renderer.hideButtons(this.gateway.messages.wrapper);
this.renderer.showButtons(this.gateway.hosted_fields.wrapper);
}
}
}
}
export default PayNowBootstrap;

View file

@ -12,7 +12,7 @@ const onApprove = (context, errorHandler) => {
if (!data.success) {
errorHandler.genericError();
console.error(data);
if (typeof actions.restart !== 'undefined') {
if (typeof actions !== 'undefined' && typeof actions.restart !== 'undefined') {
return actions.restart();
}
throw new Error(data.data.message);

View file

@ -10,7 +10,10 @@ class CreditCardRenderer {
render(wrapper, contextConfig) {
if (
(
this.defaultConfig.context !== 'checkout'
&& this.defaultConfig.context !== 'pay-now'
)
|| wrapper === null
|| document.querySelector(wrapper) === null
) {

View file

@ -108,7 +108,8 @@ return array(
},
'button.endpoint.create-order' => static function ( $container ): CreateOrderEndpoint {
$request_data = $container->get( 'button.request-data' );
$repository = $container->get( 'api.repository.cart' );
$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' );
$session_handler = $container->get( 'session.handler' );
@ -116,7 +117,8 @@ return array(
$early_order_handler = $container->get( 'button.helper.early-order-handler' );
return new CreateOrderEndpoint(
$request_data,
$repository,
$cart_repository,
$purchase_unit_factory,
$order_endpoint,
$payer_factory,
$session_handler,

View file

@ -177,6 +177,15 @@ class SmartButton implements SmartButtonInterface {
),
11
);
add_action(
'woocommerce_pay_order_after_submit',
array(
$this,
'dcc_renderer',
),
11
);
}
return true;
}
@ -232,6 +241,14 @@ class SmartButton implements SmartButtonInterface {
),
11
);
add_action(
'woocommerce_pay_order_after_submit',
array(
$this,
'message_renderer',
),
11
);
}
return true;
}
@ -294,6 +311,7 @@ class SmartButton implements SmartButtonInterface {
}
add_action( 'woocommerce_review_order_after_submit', array( $this, 'button_renderer' ), 10 );
add_action( 'woocommerce_pay_order_after_submit', array( $this, 'button_renderer' ), 10 );
return true;
}
@ -321,7 +339,7 @@ class SmartButton implements SmartButtonInterface {
$load_script = true;
}
if ( is_checkout() && $this->can_render_dcc() ) {
if ( in_array( $this->context(), array( 'pay-now', 'checkout' ), true ) && $this->can_render_dcc() ) {
wp_enqueue_style(
'ppcp-hosted-fields',
$this->module_url . '/assets/css/hosted-fields.css',
@ -474,7 +492,8 @@ class SmartButton implements SmartButtonInterface {
*/
private function can_render_dcc() : bool {
return $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) && $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) && $this->dcc_applies->for_country_currency();
$can_render = $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) && $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) && $this->dcc_applies->for_country_currency();
return $can_render;
}
/**
@ -502,6 +521,8 @@ class SmartButton implements SmartButtonInterface {
esc_html__( 'Save your card', 'paypal-payments-for-woocommerce' )
) : '';
$label = 'checkout' === $this->context() ? __( 'Place order', 'paypal-payments-for-woocommerce' ) : __( 'Pay for order', 'paypal-payments-for-woocommerce' );
printf(
'<div id="%1$s" style="display:none;">
<button class="button alt">%6$s</button>
@ -512,7 +533,7 @@ class SmartButton implements SmartButtonInterface {
esc_html__( 'CVV', 'paypal-payments-for-woocommerce' ),
//phpcs:ignore
$save_card,
esc_html__( 'Place order', 'paypal-payments-for-woocommerce' )
esc_html( $label )
);
}
@ -555,6 +576,8 @@ class SmartButton implements SmartButtonInterface {
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException If a setting hasn't been found.
*/
private function localize_script(): array {
global $wp;
$this->request_data->enqueue_nonce_fix();
$localize = array(
'script_attributes' => $this->attributes(),
@ -626,6 +649,7 @@ class SmartButton implements SmartButtonInterface {
),
),
),
'order_id' => 'pay-now' === $this->context() ? absint( $wp->query_vars['order-pay'] ) : 0,
);
if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) {
@ -818,6 +842,9 @@ class SmartButton implements SmartButtonInterface {
if ( is_checkout() && ! $this->session_handler->order() ) {
$context = 'checkout';
}
if ( is_checkout_pay_page() ) {
$context = 'pay-now';
}
return $context;
}

View file

@ -10,9 +10,11 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button\Endpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentMethod;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
@ -39,7 +41,14 @@ class CreateOrderEndpoint implements EndpointInterface {
*
* @var CartRepository
*/
private $repository;
private $cart_repository;
/**
* The PurchaseUnit factory.
*
* @var PurchaseUnitFactory
*/
private $purchase_unit_factory;
/**
* The order endpoint.
@ -87,7 +96,8 @@ class CreateOrderEndpoint implements EndpointInterface {
* CreateOrderEndpoint constructor.
*
* @param RequestData $request_data The RequestData object.
* @param CartRepository $repository The CartRepository object.
* @param CartRepository $cart_repository The CartRepository object.
* @param PurchaseUnitFactory $purchase_unit_factory The Purchaseunit factory.
* @param OrderEndpoint $order_endpoint The OrderEndpoint object.
* @param PayerFactory $payer_factory The PayerFactory object.
* @param SessionHandler $session_handler The SessionHandler object.
@ -96,7 +106,8 @@ class CreateOrderEndpoint implements EndpointInterface {
*/
public function __construct(
RequestData $request_data,
CartRepository $repository,
CartRepository $cart_repository,
PurchaseUnitFactory $purchase_unit_factory,
OrderEndpoint $order_endpoint,
PayerFactory $payer_factory,
SessionHandler $session_handler,
@ -105,7 +116,8 @@ class CreateOrderEndpoint implements EndpointInterface {
) {
$this->request_data = $request_data;
$this->repository = $repository;
$this->cart_repository = $cart_repository;
$this->purchase_unit_factory = $purchase_unit_factory;
$this->api_endpoint = $order_endpoint;
$this->payer_factory = $payer_factory;
$this->session_handler = $session_handler;
@ -131,36 +143,36 @@ class CreateOrderEndpoint implements EndpointInterface {
public function handle_request(): bool {
try {
$data = $this->request_data->read_request( $this->nonce() );
$purchase_units = $this->repository->all();
$payer = null;
if ( isset( $data['payer'] ) && $data['payer'] ) {
if ( isset( $data['payer']['phone']['phone_number']['national_number'] ) ) {
// make sure the phone number contains only numbers and is max 14. chars long.
$number = $data['payer']['phone']['phone_number']['national_number'];
$number = preg_replace( '/[^0-9]/', '', $number );
$number = substr( $number, 0, 14 );
$data['payer']['phone']['phone_number']['national_number'] = $number;
$wc_order = null;
if ( 'pay-now' === $data['context'] ) {
$wc_order = wc_get_order( (int) $data['order_id'] );
if ( ! is_a( $wc_order, \WC_Order::class ) ) {
wp_send_json_error(
array(
'name' => 'order-not-found',
'message' => __( 'Order not found', 'paypal-payments-for-woocommerce' ),
'code' => 0,
'details' => array(),
)
);
}
$payer = $this->payer_factory->from_paypal_response( json_decode( wp_json_encode( $data['payer'] ) ) );
$purchase_units = array( $this->purchase_unit_factory->from_wc_order( $wc_order ) );
} else {
$purchase_units = $this->cart_repository->all();
}
$bn_code = isset( $data['bn_code'] ) ? (string) $data['bn_code'] : '';
if ( $bn_code ) {
$this->session_handler->replace_bn_code( $bn_code );
$this->api_endpoint->with_bn_code( $bn_code );
}
$payee_preferred = $this->settings->has( 'payee_preferred' )
&& $this->settings->get( 'payee_preferred' ) ?
PaymentMethod::PAYEE_PREFERRED_IMMEDIATE_PAYMENT_REQUIRED
: PaymentMethod::PAYEE_PREFERRED_UNRESTRICTED;
$payment_method = new PaymentMethod( $payee_preferred );
$this->set_bn_code( $data );
$order = $this->api_endpoint->create(
$purchase_units,
$payer,
$this->payer( $data, $wc_order ),
null,
$payment_method
$this->payment_method()
);
if ( 'checkout' === $data['context'] ) {
$this->validateForm( $data['form'], $order );
$this->validate_checkout_form( $data['form'], $order );
}
if ( 'pay-now' === $data['context'] ) {
$this->validate_paynow_form( $data['form'] );
}
wp_send_json_success( $order->to_array() );
return true;
@ -177,6 +189,64 @@ class CreateOrderEndpoint implements EndpointInterface {
}
}
/**
* Returns the Payer entity based on the request data.
*
* @param array $data The request data.
* @param \WC_Order $wc_order The order.
*
* @return Payer|null
*/
private function payer( array $data, \WC_Order $wc_order = null ) {
if ( 'pay-now' === $data['context'] ) {
$payer = $this->payer_factory->from_wc_order( $wc_order );
return $payer;
}
$payer = null;
if ( isset( $data['payer'] ) && $data['payer'] ) {
if ( isset( $data['payer']['phone']['phone_number']['national_number'] ) ) {
// make sure the phone number contains only numbers and is max 14. chars long.
$number = $data['payer']['phone']['phone_number']['national_number'];
$number = preg_replace( '/[^0-9]/', '', $number );
$number = substr( $number, 0, 14 );
$data['payer']['phone']['phone_number']['national_number'] = $number;
}
$payer = $this->payer_factory->from_paypal_response( json_decode( wp_json_encode( $data['payer'] ) ) );
}
return $payer;
}
/**
* Sets the BN Code for the following request.
*
* @param array $data The request data.
*/
private function set_bn_code( array $data ) {
$bn_code = isset( $data['bn_code'] ) ? (string) $data['bn_code'] : '';
if ( ! $bn_code ) {
return;
}
$this->session_handler->replace_bn_code( $bn_code );
$this->api_endpoint->with_bn_code( $bn_code );
}
/**
* Returns the PaymentMethod object for the order.
*
* @return PaymentMethod
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException In case a setting would not be found.
*/
private function payment_method() : PaymentMethod {
$payee_preferred = $this->settings->has( 'payee_preferred' ) && $this->settings->get( 'payee_preferred' ) ?
PaymentMethod::PAYEE_PREFERRED_IMMEDIATE_PAYMENT_REQUIRED
: PaymentMethod::PAYEE_PREFERRED_UNRESTRICTED;
$payment_method = new PaymentMethod( $payee_preferred );
return $payment_method;
}
/**
* Prepare the Request parameter and process the checkout form and validate it.
*
@ -185,7 +255,7 @@ class CreateOrderEndpoint implements EndpointInterface {
*
* @throws \Exception On Error.
*/
private function validateForm( string $form_values, Order $order ) {
private function validate_checkout_form( string $form_values, Order $order ) {
$this->order = $order;
$parsed_values = wp_parse_args( $form_values );
$_POST = $parsed_values;
@ -204,6 +274,21 @@ class CreateOrderEndpoint implements EndpointInterface {
$checkout->process_checkout();
}
/**
* Checks whether the terms input field is checked.
*
* @param string $form_values The form values.
* @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'] ) ) {
throw new \RuntimeException(
__( 'Please read and accept the terms and conditions to proceed with your order.', 'paypal-payments-for-woocommerce' )
);
}
}
/**
* Once the checkout has been validated we execute this method.
*