-
Notifications
You must be signed in to change notification settings - Fork 219
Need a working example for a simple checkbox along with server side hooks with customer and cart data #5179
Comments
Hey there @ryanhungate This PR provides a working example for how MailPoet newsletter opt in integration works The gist of it is that you will create a new JS block, that loads both on the editor and frontend (two versions of the same block). In the frontend script, you register your component using This guide should help you register that block. This tutorial should you create a dynamic JS block. The sample repo is slightly outdated and so needs an update. So to summarize:
Mailpoet example should guide you through the process.
Those things are passed to your frontend component as props,
The value you register in the frontend, for example using add_action(
'woocommerce_blocks_checkout_update_order_from_request',
[$this, 'process_checkout'],
10,
2
);
function process_checkout( $order, $request) {
$request['extensions']['mailchimp']['optin'];
// your logic.
} But for it to be on the request, you need to register it on schema, using use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CheckoutSchema;
public function extend_rest_api() {
$extend = Package::container()->get(ExtendRestApi::class);
$extend->register_endpoint_data(
[
'endpoint' => CheckoutSchema::IDENTIFIER,
'namespace' => 'mailchimp',
'schema_callback' => function () {
return [
'optin' => [
'description' => __('Subscribe to marketing opt-in.', 'mailchimp'),
'type' => 'boolean',
],
];
},
]
);
} |
@senadir thank you so much for the help on this... I will get into this today and hopefully you gave me all the clues I needed to finish out our part! :) 👍 I will report back after a few hours and will close this out if we're good. |
@ryanhungate this is a working example of a newsletter plugin https://github.com/opr/newsletter-test/tree/update/to-latest-working-api It doesn't contain any frontend validation or form blocking, if you're looking for that, take a look at the terms and conditions block |
From what I understood, you're looking at adding more than just a checkbox right? Are you trying to add text fields as well? |
@senadir our plugin renders out dynamic checkboxes based on the store owner's GDPR fields on the Mailchimp audience... so basically it is not hard coded. We make an API call to the Mailchimp API - and then dynamically render the checkboxes along with the labels on the older checkout screen. So ideally if there was a way to pass in an array of objects to the component that could add additional checkboxes at runtime, that's really what we're after yes. Thanks so much for your help. Very much appreciated here. Also I'll take a look at that working repo you showed me because I think I was using the master version which wasn't. |
Can this condition be evaluated on the server, and then just pass the data with |
@senadir yeah for sure. That's what we're currently doing in the standard checkout screens, so if there was a way for us to pass in these options to the component registration somehow that's really all we're after... |
@senadir i wanted to hold off to say something till I had a chance to get this thing implemented in our staging environment - and here's the current findings. Code looks great, very well organized and easy to understand - so thank you very much for this - but I'm seeing some issues that I don't understand yet.
This is as far as I've gotten for the day - but i'm sure tomorrow I'll be able to get deep into this and resolve all the issues. I think overall this is close to a perfect starting point so I did want to thank you again for that. |
Hey, it seems your editor script is loading on the frontend for some reason? only the frontend script should load there. Can you show me the code you have that enqueues/registers your editor scripts? Preferably, you shouldn't enqueue anything yourself for the editor, |
@senadir i used the exact code in your example repository... :) Changed up a couple names to avoid conflicts but it's the same otherwise. |
@senadir it's broken on the front end - not the back end editor if that matters. |
Yes, I know. It's weird that this is breaking your frontend instead of just a warning. |
@senadir yeah the console error went away - but this is what i'm seeing on the checkout screen. Almost like it's still in a loading state. No errors at all, and I also see the newsletter additions in the source - so it looks as if it's loading correctly... this is really odd. Have you seen this before? |
@senadir i think this had something to do with an addition I had added - but didn't see any error messages so it was invisible. I need to render out an array of GDPR fields into this component too. Do you have a snippet that shows how to pass in an array to the component so we can load dynamic checkboxes at all? Sorry for all the back and forth - but we're making a lot of headway so It's much appreciated what you've been doing. |
@senadir do you happen to know the hook for grabbing the cart when it's updated? This new checkout seems to be bypassing the typical order and cart hooks we're used to getting. Looks like a really nice move forward - but yeah just need to know how to accomplish the same things as the traditional stores did if you have time. Thanks! |
The cart content is passed as const Block = ( { cart, text, checkoutExtensionData } ) => {
const { billingAddress, shippingAddress } = cart;
const [ checked, setChecked ] = useState( false );
const { setExtensionData } = checkoutExtensionData; To pass data from the server to your component, you use You would add another After doing that, you will get access to your data in your block in frontend, a new prop const Block = ( { cart, extensions, text, checkoutExtensionData } ) => {
const { mailchimp } = extensions;
const { gdprFields } = mailchimp;
const { billingAddress, shippingAddress } = cart;
const [ checked, setChecked ] = useState( false );
const { setExtensionData } = checkoutExtensionData; |
The error you're seeing means the Checkout never finished loading, are you seeing any console errors, server errors, scripts not loading? |
@senadir ok - great... this is starting to make sense to me. Thanks. So I guess I would need to just make an ajax request to the server with the cart details inside this component ( to capture the cart and push to Mailchimp as abandoned )... right? There's no pre-built hook that would allow us to use what's currently being pushed to the server for this information? I didn't want to double up on the requests if there was already a "cart updated" hook that you're already providing. |
There's no "cart updated" hook, your Preferably, if you can explain to me how you imagine the integration would work, or how does it work with Checkout shortcode, then I can point down all the various extensibility patterns we have.
There are other extensibility APIs, like forcing the cart to refetch if you want. |
@senadir we need to get the cart data on the server side, not the client side. Reason being is that we need to pass over the cart details to Mailchimp so the customers can use the "abandoned cart" mail feature. Does this use the same woo session for the cart, or is it using an entirely different storage system? Same thing for the "orders being created" it seems as if the hooks are different as well? |
It uses the same, so you can just use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
$cart_controller = new CartController();
$cart = $cart_controller->get_cart_instance(); For hooks, we have new hooks since the interface passed to them changed (WP_Request object instead of $_POST). Those two should do the work (all of them here).
Checkout block uses draft orders so an order is created much earlier compared to Checkout shortcode. In shortcode, the order is created once you place an order, in block, it's created once you visit the checkout, when you hit submit, |
@senadir ok I think this is where i'm confused because the hook |
Hmm, that's true, it doesn't trigger for That's the code I used: /**
* Store guest info when they submit email from Store API.
*
* The guest email, first name and last name are captured.
*
* @see \Automattic\WooCommerce\Blocks\StoreApi\Routes\CartUpdateCustomer
*
* @param \WC_Order $order
*/
public static function capture_from_store_api( $order ) {
if ( ! Options::presubmit_capture_enabled() || is_user_logged_in() ) {
return;
}
if ( $order->get_status() !== 'checkout-draft' || ! $order->is_created_via( 'store-api' ) || ! $order->get_billing_email() || $order->get_customer_id() ) {
return;
}
$customer = self::set_session_by_captured_email( $order->get_billing_email() );
if ( $customer ) {
// Capture the guest's name
$guest = $customer->get_guest();
if ( $guest ) {
$guest->update_meta( 'billing_first_name', $order->get_billing_first_name() );
$guest->update_meta( 'billing_last_name', $order->get_billing_last_name() );
}
}
} |
@senadir ok - this last snippet is great... exactly what I needed. Sorry to bother again but is there any chance of getting the extension values in this hook as well? I just realized this could solve another problem we were trying to fix where people only wanted cart messages to go to folks that are marked as subscribed. This would be great if I could access the extension values here too? |
I'm not sure I understand this question. You can't access the $request object on |
@senadir yeah basically the concept is, hook into the We actually have customers that wanted to limit the cart submissions to the ones that had the checkbox checked. Is there any other hook that we could use or a method on the server side which would show this extension data? |
You can prevent order from being submitted by adding validation to your checkbox, I linked to it before but here's how the terms and conditions checkbox prevent order submitting if it's not checked: Alternatively, you can reject submission at |
@senadir sorry I'm not sure if my question got jumbled - but what I meant is that during the checkout process, while an order is still in a "draft" state, we would consider this "abandoned"... and during this step of creating the draft order, it would be great if we could see the extensions current value as well. Does that make sense? The newsletter checkbox being checked at this stage ( before the order is placed ) would allow us to capture the subscriber status, and allow store owners that are concerned with GDPR that they would only send cart messages to people that were subscribed as well. So not having this value makes us have to implement an ajax request and store something in session every time the checkbox is changed - which we can do of course, but just wanted to try and avoid this if at all possible too using proper hooks already being sent to the server side. |
If I understand correctly, you want to capture abandoned carts, but also if the user consented to being contacted by checking the checkbox? You can react the email field being filled, but not to the checkbox being checked, not before placing an order. It also depends if you're going to store the merchant opt it in a persistent place (DB) or just in session until they actually submit the order? What I would do in this case is: 1- Register opt in status in the cart response using use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
public function extend_rest_api() {
$extend = Package::container()->get(ExtendRestApi::class);
$extend->register_endpoint_data(
[
'endpoint' => CartSchema::IDENTIFIER,
'namespace' => 'mailchimp',
'schema_callback' => function () {
return [
'is_opted_in' => [
'description' => __('Has Subscribed to marketing opt-in.', 'mailchimp'),
'type' => 'boolean',
],
];
},
'data_callback' => function () {
return [
'is_opted_in' => is_current_customer_opted_in(),
];
},
]
);
} const Block = ( { cart, extensions, text, } ) => {
const { mailchimp } = extensions;
const { is_opted_in } = mailchimp;
import { extensionCartUpdate } from '@woocommerce/blocks-checkout';
const Block = ( { cart, extensions, text, } ) => {
const { mailchimp } = extensions;
const { is_opted_in } = mailchimp;
const [ checked, setChecked ] = useState( is_opted_in )l
const buttonClickHandler = () => {
extensionCartUpdate( {
namespace: 'mailchimp',
data: {
checked: ! checked,
},
} );
setChecked( ! checked );
};
return <Checkbox checked={ checked } onClick={ buttonClickHandler } /> use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
public function extend_rest_api() {
$extend = Package::container()->get(ExtendRestApi::class);
$extend->register_endpoint_data(
[
'endpoint' => CartSchema::IDENTIFIER,
'namespace' => 'mailchimp',
'schema_callback' => function () {
return [
'is_opted_in' => [
'description' => __('Has Subscribed to marketing opt-in.', 'mailchimp'),
'type' => 'boolean',
],
];
},
'data_callback' => function () {
return [
'is_opted_in' => is_current_customer_opted_in(),
];
},
]
);
$extend->register_update_callback(
[
'namespace' => 'mailchimp',
'callback' => function ( $data ) {
persist_customer_optin( $data['checked'] );
}
]
);
} Now, your optin is persisted, and is updated each that checkbox is clicked, you probably don't need |
Hi @ryanhungate, As a part of this repository’s maintenance, I am closing this issue due to inactivity. Please feel free to comment on it in case we missed something. We’d be happy to take another look. |
Is your feature request related to a problem? Please describe.
The Mailchimp for Woocommerce plugin needs the ability to inject a checkbox for the "subscriber status" and "GDPR" related fields into the checkout form and save the data on the customer and order records.
Describe the solution you'd like
Describe alternatives you've considered
We have looked at this sample repo but still not sure how to use the example on the server side - getting errors as of an update today (11/17/2021):
Additional context
This is a blocking issue for the Mailchimp customers because we cannot capture the newsletter status of each customer. We're very well versed in React and VUE and PHP - but at this point just a bit confused on how to implement a new feature in this ecosystem and need some help :)
The text was updated successfully, but these errors were encountered: