Skip to content

Commit

Permalink
Merge pull request #14 from ndeet/modal-checkout
Browse files Browse the repository at this point in the history
Add modal checkout mode.
  • Loading branch information
ndeet authored Apr 3, 2023
2 parents 8cca268 + 46eee0c commit cc204a8
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 20 deletions.
188 changes: 188 additions & 0 deletions assets/js/modalCheckout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
jQuery(function ($) {
/**
* Main entry point.
*/
// Listen on Update cart and change of payment methods.
$('body').on('init_checkout updated_checkout payment_method_selected', function (event) {
if (BTCPayWP.modalEnabled == 1) {
btcpaySelected();
}
});

/**
* Trigger ajax request to create order object and assign an invoice id.
*/
var processOrder = function () {

let responseData = null;

// Block the UI.
blockElement('.woocommerce-checkout-payment');

// Prepare form data and additional required data.
let formData = $('form.checkout').serialize();
let additionalData = {
'action': 'btcpaygf_modal_checkout',
'apiNonce': BTCPayWP.apiNonce,
};

let data = $.param(additionalData) + '&' + formData;

// We need to make sure the order processing worked before returning from this function.
$.ajaxSetup({async: false});

$.post(wc_checkout_params.checkout_url, data, function (response) {
//console.log('Received response when processing order: ');
//console.log(response);

if (response.invoiceId) {
responseData = response;
} else {
unblockElement('.woocommerce-checkout-payment');
// Show errors.
if (response.messages) {
submitError(response.messages);
} else {
submitError('<div class="woocommerce-error">' + wc_checkout_params.i18n_checkout_error + '</div>'); // eslint-disable-line max-len
}
}
}).fail(function () {
unblockElement('.woocommerce-checkout-payment');
submitError('<div class="woocommerce-error">' + wc_checkout_params.i18n_checkout_error + '</div>');
});

// Reenable async.
$.ajaxSetup({async: true});

return responseData;
};

/**
* Show the BTCPay modal and listen to events sent by BTCPay server.
*/
var showBTCPayModal = function(data) {
//console.log('Triggered showBTCPayModal()');

if (data.invoiceId !== undefined) {
window.btcpay.setApiUrlPrefix(BTCPayWP.apiUrl);
window.btcpay.showInvoice(data.invoiceId);
}
let invoice_paid = false;
window.btcpay.onModalReceiveMessage(function (event) {
if (isObject(event.data)) {
//console.log('BTCPay modal event: invoiceId: ' + event.data.invoiceId);
//console.log('BTCPay modal event: status: ' + event.data.status);
if (event.data.status) {
switch (event.data.status) {
case 'complete':
case 'paid':
invoice_paid = true;
window.location = data.orderCompleteLink;
break;
case 'expired':
window.btcpay.hideFrame();
submitError(BTCPayWP.textInvoiceExpired);
break;
}
}
} else { // handle event.data "loaded" "closed"
if (event.data === 'close') {
if (invoice_paid === true) {
window.location = data.orderCompleteLink;
}
submitError(BTCPayWP.textModalClosed);
}
}
});
const isObject = obj => {
return Object.prototype.toString.call(obj) === '[object Object]'
}
}

/**
* Block UI of a given element.
*/
var blockElement = function (cssClass) {
//console.log('Triggered blockElement.');

$(cssClass).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
};

/**
* Unblock UI of a given element.
*/
var unblockElement = function (cssClass) {
//console.log('Triggered unblockElement.');
$(cssClass).unblock();
};

/**
* Show errors, mostly copied from WC checkout.js
*
* @param error_message
*/
var submitError = function (error_message) {
let $checkoutForm = $('form.checkout');
$('.woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message').remove();
$checkoutForm.prepend('<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout woocommerce-error">' + error_message + '</div>'); // eslint-disable-line max-len
$checkoutForm.removeClass('processing').unblock();
$checkoutForm.find('.input-text, select, input:checkbox').trigger('validate').trigger('blur');
scrollToNotices();
$(document.body).trigger('checkout_error', [error_message]);
unblockElement('.woocommerce-checkout-payment');
};

/**
* Scroll to errors on top of form, copied from WC checkout.js.
*/
var scrollToNotices = function () {
var scrollElement = $('.woocommerce-NoticeGroup-updateOrderReview, .woocommerce-NoticeGroup-checkout');

if (!scrollElement.length) {
scrollElement = $('form.checkout');
}

$.scroll_to_notices(scrollElement);
};

/**
* Trigger payframe button submit.
*/
var submitOrder = function (e) {
e.preventDefault();
//console.log('Triggered submitOrder');

let responseData = processOrder();
if (responseData) {
//console.log('Got invoice, opening modal.');
blockElement('.woocommerce-checkout-payment');
showBTCPayModal(responseData);
}

return false;
};

/**
* Makes sure to trigger on payment method changes and overriding the default button submit handler.
*/
var btcpaySelected = function () {
var checkout_form = $('form.woocommerce-checkout');
var selected_gateway = $('form[name="checkout"] input[name="payment_method"]:checked').val();
unblockElement('.woocommerce-checkout-payment');

if (selected_gateway.startsWith('btcpaygf_')) {
// Bind our custom event handler to the checkout button.
checkout_form.on('checkout_place_order', submitOrder);
} else {
// Unbind custom event handlers.
checkout_form.off('checkout_place_order', submitOrder);
}
}

});
27 changes: 27 additions & 0 deletions btcpay-greenfield-for-woocommerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function __construct() {
$this->includes();

add_action('woocommerce_thankyou_btcpaygf_default', ['BTCPayServerWCPlugin', 'orderStatusThankYouPage'], 10, 1);
add_action( 'wp_ajax_btcpaygf_modal_checkout', [$this, 'processAjaxModalCheckout'] );
add_action( 'wp_ajax_nopriv_btcpaygf_modal_checkout', [$this, 'processAjaxModalCheckout'] );

// Run the updates.
\BTCPayServer\WC\Helper\UpdateManager::processUpdates();
Expand Down Expand Up @@ -204,6 +206,31 @@ public function processAjaxApiUrl() {
wp_send_json_error("Error processing Ajax request.");
}

/**
* Handles the AJAX callback from the Payment Request on the checkout page.
*/
public function processAjaxModalCheckout() {

Logger::debug('Entering ' . __METHOD__);

$nonce = $_POST['apiNonce'];
if ( ! wp_verify_nonce( $nonce, 'btcpay-nonce' ) ) {
wp_die('Unauthorized!', '', ['response' => 401]);
}

if ( get_option('btcpay_gf_modal_checkout') !== 'yes' ) {
wp_die('Modal checkout mode not enabled.', '', ['response' => 400]);
}

wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );

try {
WC()->checkout()->process_checkout();
} catch (\Throwable $e) {
Logger::debug('Error processing modal checkout ajax callback: ' . $e->getMessage());
}
}

public static function orderStatusThankYouPage($order_id)
{
if (!$order = wc_get_order($order_id)) {
Expand Down
35 changes: 21 additions & 14 deletions src/Admin/GlobalSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,51 +54,51 @@ public function getGlobalSettings(): array
{
Logger::debug('Entering Global Settings form.');
return [
'title' => [
'title' => [
'title' => esc_html_x(
'BTCPay Server Payments Settings',
'global_settings',
'btcpay-greenfield-for-woocommerce'
),
'type' => 'title',
'type' => 'title',
'desc' => sprintf( _x( 'This plugin version is %s and your PHP version is %s. Check out our <a href="https://docs.btcpayserver.org/WooCommerce/" target="_blank">installation instructions</a>. If you need assistance, please come on our <a href="https://chat.btcpayserver.org" target="_blank">chat</a>. Thank you for using BTCPay!', 'global_settings', 'btcpay-greenfield-for-woocommerce' ), BTCPAYSERVER_VERSION, PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION ),
'id' => 'btcpay_gf'
],
'url' => [
'title' => esc_html_x(
'url' => [
'title' => esc_html_x(
'BTCPay Server URL',
'global_settings',
'btcpay-greenfield-for-woocommerce'
),
'type' => 'text',
'type' => 'text',
'desc' => esc_html_x( 'URL/host to your BTCPay Server instance. Note: if you use a self hosted node like Umbrel, RaspiBlitz, myNode, etc. you will have to make sure your node is reachable from the internet. You can do that through <a href="https://docs.btcpayserver.org/Deployment/ReverseProxyToTor/" target="_blank">Tor</a>, <a href="https://docs.btcpayserver.org/Docker/cloudflare-tunnel/" target="_blank">Cloudflare</a> or <a href="https://docs.btcpayserver.org/Deployment/ReverseSSHtunnel/" target="_blank">SSH (advanced)</a>.', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'placeholder' => esc_attr_x( 'e.g. https://btcpayserver.example.com', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'desc_tip' => true,
'id' => 'btcpay_gf_url'
],
'api_key' => [
'api_key' => [
'title' => esc_html_x( 'BTCPay API Key', 'global_settings','btcpay-greenfield-for-woocommerce' ),
'type' => 'text',
'desc' => _x( 'Your BTCPay API Key. If you do not have any yet <a href="#" class="btcpay-api-key-link" target="_blank">click here to generate API keys.</a>', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'default' => '',
'id' => 'btcpay_gf_api_key'
],
'store_id' => [
'store_id' => [
'title' => esc_html_x( 'Store ID', 'global_settings','btcpay-greenfield-for-woocommerce' ),
'type' => 'text',
'desc_tip' => _x( 'Your BTCPay Store ID. You can find it on the store settings page on your BTCPay Server.', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'default' => '',
'id' => 'btcpay_gf_store_id'
],
'default_description' => [
'default_description' => [
'title' => esc_html_x( 'Default Customer Message', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'textarea',
'desc' => esc_html_x( 'Message to explain how the customer will be paying for the purchase. Can be overwritten on a per gateway basis.', 'btcpay-greenfield-for-woocommerce' ),
'default' => esc_html_x('You will be redirected to BTCPay to complete your purchase.', 'global_settings', 'btcpay-greenfield-for-woocommerce'),
'desc_tip' => true,
'id' => 'btcpay_gf_default_description'
],
'transaction_speed' => [
'transaction_speed' => [
'title' => esc_html_x( 'Invoice pass to "settled" state after', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'select',
'desc' => esc_html_x('An invoice becomes settled after the payment has this many confirmations...', 'global_settings', 'btcpay-greenfield-for-woocommerce'),
Expand All @@ -113,32 +113,39 @@ public function getGlobalSettings(): array
'desc_tip' => true,
'id' => 'btcpay_gf_transaction_speed'
],
'order_states' => [
'order_states' => [
'type' => 'order_states',
'id' => 'btcpay_gf_order_states'
],
'separate_gateways' => [
'modal_checkout' => [
'title' => __( 'Modal checkout', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
'desc' => _x( 'Opens a modal overlay on the checkout page instead of redirecting to BTCPay Server.', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'id' => 'btcpay_gf_modal_checkout'
],
'separate_gateways' => [
'title' => __( 'Separate Payment Gateways', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
'desc' => _x( 'Make all supported and enabled payment methods available as their own payment gateway. This opens new possibilities like discounts for specific payment methods. See our <a href="https://docs.btcpayserver.org/FAQ/Integrations/#how-to-configure-additional-token-support-separate-payment-gateways" target="_blank">full guide here</a>', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'id' => 'btcpay_gf_separate_gateways'
],
'customer_data' => [
'customer_data' => [
'title' => __( 'Send customer data to BTCPayServer', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
'desc' => _x( 'If you want customer email, address, etc. sent to BTCPay Server enable this option. By default for privacy and GDPR reasons this is disabled.', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'id' => 'btcpay_gf_send_customer_data'
],
'sats_mode' => [
'sats_mode' => [
'title' => __( 'Sats-Mode', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
'desc' => _x( 'Makes Satoshis/Sats available as currency "SAT" (can be found in WooCommerce->Settings->General) and handles conversion to BTC before creating the invoice on BTCPay.', 'global_settings', 'btcpay-greenfield-for-woocommerce' ),
'id' => 'btcpay_gf_sats_mode'
],
'debug' => [
'debug' => [
'title' => __( 'Debug Log', 'btcpay-greenfield-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
Expand Down
Loading

0 comments on commit cc204a8

Please sign in to comment.