-
Notifications
You must be signed in to change notification settings - Fork 815
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simple Payments: CPTs and Shortcode #7409
Conversation
* spay_currency - currency code | ||
* spay_cta - text with "Buy" or other CTA | ||
* spay_email - paypal email | ||
* spay_multiple - allow for multiple items |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also probably store if a product is "enabled" or "disabled" (if it should show or show out of stock).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added "status".
Except for the one comment I left it looks good to me. We can always add/remove stuff if needed later on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the approach you took here! Great work.
Left a few smaller comments.
static $post_type_order = 'jp_pay_order'; | ||
static $post_type_product = 'jp_pay_product'; | ||
|
||
static $shortcode = 'simple-payment'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe simple-payments
? That's how we are calling it internally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but the shortcode represents a single simple payment, so it would be weird to have:
[simple-payments id=43]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not weird for me. It's a product name (like videopress), not a real English word. It would make sense in case of [paypal-button id...]
but [simple-payment]
is strange as the product is called simple-payments
.
/cc @jhnstn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is precedence for using the product name even if it's a plural form e.g. [8tracks ...]
or [googleapps ...]
for shortcodes available on wpcom https://en.support.wordpress.com/shortcodes/
But those are highly branded services that we can't change.
I can't find anything specific around best practices here https://developer.wordpress.org/plugins/shortcodes/#shortcode-best-practices
There is the prefix thing so something like [jetpack-simple-payments ]
would make sense to use the full product name. Not saying we need the prefix however.
I do find it more natural to use the singular form but the plural form is more correct. That said I suggest we go with [simple-payment]
since it's more user friendly.
} | ||
|
||
private function register_scripts() { | ||
wp_register_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make a local copy of the file or is this the only way to load it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Paypal heavily discourages it, but Im on the fence here.
maybe it is better to load this from server?
But we would have to keep it updated, so I dont know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I see. Let's use it from their servers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should delegate the source of this file straight to the Paypal server. I think we should keep a copy of the latest stable version and find a way to update it when it's needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally speaking I wouldn't want to source from a third party but in this case I think we should start with PayPal's recommendation to source the script from them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 At the very least we should probably leave a comment in the source here with a brief explanation and a link to that documentation.
|
||
private function register_scripts() { | ||
wp_register_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js' ); | ||
wp_register_script( 'paypal-express-checkout', plugins_url( '/paypal-express-checkout.js', __FILE__ ) , array( 'paypal-checkout-js' ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this script? On https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/server-side-REST-integration/, I see only the first one. Or is it our own JS script for this shortcode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/paypal-express-checkout.js
is our own JS script and we need it.
'dom_id' => uniqid( 'jp_simple_payments__button_' . $product->ID . '_' ), | ||
'class' => 'jp_simple_payments__' . $product->ID, | ||
'title' => get_the_title( $product ), | ||
'description' => $product->post_content, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should also run it though the the_content
filter so it behaves the same like when rendering a post:
$content = $content_post->post_content;
$content = apply_filters('the_content', $content);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do
'class' => 'jp_simple_payments__' . $product->ID, | ||
'title' => get_the_title( $product ), | ||
'description' => $product->post_content, | ||
'cta' => get_post_meta( $product->ID, 'spay_cta', true ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still want this? Since Paypal won't allow to change the CTA text.
$data | ||
); | ||
|
||
wp_enqueue_script( 'paypal-express-checkout' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to enqueue this only once per page -- what if there are multiple simple payments shortcodes on one page?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wp_enqueue_script works in a way that you can call it multiple times, but it will enqueue only once
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this is true, it's better and faster to use wp_script_is
to avoid all the processing wp_enqueue_script
does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently attempting to install a git version of jetpack on my jetpack sandbox; it's taking a while but I figured I'll leave my comments now and report on the testing later.
createPaymentEndpoint: '', //TODO: point to the actual endpoint | ||
executePaymentEndpoint: '' //TODO: point to the actual endpoint | ||
}, | ||
renderButton: function( id ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for defensive coding purposes (like if somehow this got included without the paypal
global), do you think it would be helpful to add if ( ! paypal ) { throw new Error( 'paypal module is required by PaypalExpressCheckout' ) }
or if ( ! paypal ) { console.error( 'paypal module is required by PaypalExpressCheckout' ) && return }
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
// TODO: handle success, errors, messaging, etc, etc. | ||
/* jshint ignore:start */ | ||
console.log( 'payment: ', payment ); | ||
alert( 'success!' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make sure to avoid committing this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
Nobody has access to that code either way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If nobody has access then it's one more reason to not commit it.
} | ||
|
||
private function register_scripts() { | ||
wp_register_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 At the very least we should probably leave a comment in the source here with a brief explanation and a link to that documentation.
'description' => $product->post_content, | ||
'cta' => get_post_meta( $product->ID, 'spay_cta', true ), | ||
), $attrs ); | ||
$data[ 'price' ] = $this->format_price( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big deal but the WP code guidelines say to not put spaces inside array reference brackets if the key is a string (which is annoying since they say the opposite for JavaScript). There's a few other instances of this syntax in this file. A code linter should notice them.
* @return array | ||
*/ | ||
function allow_rest_api_types( $post_types ) { | ||
$post_types[] = self::$post_type_order; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't the []=
shorthand introduced in php 5.3? Maybe I'm wrong; I've always thought it was, but I'm having a hard time finding evidence. (If we are following WP core coding standards, we can't use features newer than PHP 5.2.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shorthand was since 4 I think.
The one introduced later is $var = [];
color: 'blue' | ||
}, | ||
payment: function() { | ||
return paypal.request.post( PaypalExpressCheckout.constants.createPaymentEndpoint ).then( function( data ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
paypal.request.post
accepts post data as the second argument: https://github.com/paypal/paypal-checkout/blob/master/docs/http.md#windowpaypalrequestposturl-data-options
I know we don't have the wpcom REST endpoints ready but might be a good time to add what we want to send to the createPaymentEndpoint
.
I'm not sure what we can derive from the request but I'd imagine we'd want to send something like:
{
button_id: 123,
post_id: 456,
button_metadata: { ... } // for quantity etc
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely out of the scope of this PR, lets add other stuff in another one.
This is monster already
b1f6e11
to
ed752a8
Compare
} | ||
$output = <<<TEMPLATE | ||
<div class="{$data[ 'class' ]} jp_simple_payments__wrapper"> | ||
<h2 class="jp_simple_payments__title">{$data['title']}</h2> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd avoid using headings here. Post titles are often h2
on posts and archive pages while they are h1
on single post page. Also themes have heading styles that are likely too big with a product image. I think using just div
would be safer and would look ok in many themes.
$items='<div class="jp_simple_payments__items" ><input type="number" value="1"></div>'; | ||
} | ||
$output = <<<TEMPLATE | ||
<div class="{$data[ 'class' ]} jp_simple_payments__wrapper"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should avoid using underscores for selectors: https://make.wordpress.org/core/handbook/best-practices/coding-standards/css/#selectors
Wouldn't jetpack-
be better than just jp-
?
0a9a506
to
0049c0d
Compare
return 'https://public-api.wordpress.com/wpcom/v2/sites/' + blogId + '/simple-payments/paypal/payment'; | ||
}, | ||
getExecutePaymentEndpoint: function( blogId, paymentId ) { | ||
return 'https://public-api.wordpress.com/wpcom/v2/sites/' + blogId + '/simple-payments/paypal/' + paymentId + '/execute'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split to two lines pls.
if ( ! numberField ) { | ||
return 1; | ||
} | ||
number = Number( numberField.value ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or parseInt
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lamosty depending on what he wants:
parseInt( '12' )
> 12
Number( '12' )
> 12
parseInt( '12ab' )
> 12
Number( '12ab' )
> NaN
he's using isNaN
below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseInt
returns NaN
as well in some cases:
An integer number parsed from the given string. If the first character cannot be converted to a number, NaN is returned. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt)
But yeah, if parseInt( '12ab' )
still produces a number, I would consider that a bad behavior so let's stick with Number()
.
}, | ||
renderButton: function( blogId, buttonId, domId, enableMultiple ) { | ||
if ( ! paypal ) { | ||
throw new Error( 'PayPal module is required by PaypalExpressCheckout' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure we want to throw a JS error here. Wouldn't it stop other JS execution on a page? Maybe a bit destructive in that case.
number: PaypalExpressCheckout.getNumberOfItems( domId + '_number', enableMultiple ), | ||
buttonId: buttonId | ||
}; | ||
return paypal.request.post( PaypalExpressCheckout.getCreatePaymentEndpoint( blogId ), payload ).then( function( data ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split to two lines pls.
b5bbf30
to
269e5eb
Compare
'dom_id' => uniqid( 'jetpack-simple-payments-' . $product->ID . '_' ), | ||
'class' => 'jetpack-simple-payments-' . $product->ID, | ||
'title' => get_the_title( $product ), | ||
'description' => apply_filters( 'the_content', $product->post_content ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add
/** This filter is already documented in core/wp-includes/post-template.php */
above this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are dropping that filter since we don't want to have HTML tags in description
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some changes to be made, but overall good work!
|
||
// We allow for overriding the presentation labels | ||
$data = shortcode_atts( array( | ||
'blog_id' => $blog_id = Jetpack_Options::get_option( 'id' ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assignment to $blog_id
is unnecessary, it's never used on its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// We allow for overriding the presentation labels | ||
$data = shortcode_atts( array( | ||
'blog_id' => $blog_id = Jetpack_Options::get_option( 'id' ), | ||
'dom_id' => uniqid( 'jetpack-simple-payments-' . $product->ID . '_' ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uniqid
might not return a unique value on some systems. At least the second parameter of uniqid
should be set to true.
However, is there a better alternative? Maybe keeping an internal static index in the class? Or anything else that ensures a truly unique id=
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not store this data. It used only while trying by mistake to use the same product button multiple times on a page. That really should not be a regular case
$data['price'] = $this->format_price( | ||
get_post_meta( $product->ID, 'spay_price', true ), | ||
get_post_meta( $product->ID, 'spay_currency', true ), | ||
$data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're passing 3 parameters here, but format_price
method only admits 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added handling 3rd argument to the function.
$data | ||
); | ||
|
||
wp_enqueue_script( 'paypal-express-checkout' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this is true, it's better and faster to use wp_script_is
to avoid all the processing wp_enqueue_script
does.
} | ||
|
||
function output_shortcode( $data ) { | ||
$items=""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add spacing around the operator and single quotes instead of double.
if( $data['multiple'] ) { | ||
$items="<div class='jetpack-simple-payments-items'> | ||
<input class='jetpack-simple-payments-items-number' type='number' value='1' id='{$data['dom_id']}_number'> | ||
</div>"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this trailing space after </div>
ok?
{$items} | ||
<div class="jetpack-simple-payments-button" id="{$data['dom_id']}_button"></div> | ||
</div> | ||
TEMPLATE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this isn't a double quoted string like the previous one? I think it can be solved in the same way and be more consistent in the code.
|
||
function format_price( $price, $currency ) { | ||
// TODO: better price formatting logic. Extracting from woocmmerce is not a solution since its bound with woo site options. | ||
return $price. " " . $currency; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs space before first .
and empty strings should always be single quoted.
In any case, this can be written as return "$price $currency";
); | ||
$order_args = array( | ||
'label' => __( 'Order', 'jetpack' ), | ||
'description' => __( 'Simple Payments orders', 'jetpack' ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please always use esc_html__
, trust no one, including translations.
); | ||
$product_args = array( | ||
'label' => __( 'Product', 'jetpack' ), | ||
'description' => __( 'Simple Payments products', 'jetpack' ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, use esc_html__
please
I implemented review fixes |
456c39e
to
893bbb3
Compare
Attempt to make test pass in JS
893bbb3
to
5214211
Compare
This is Jetpack part of architecture described in p8RSuw-2E-code #comment-51
After this is merged in JP, we will port it to wpcom.
We will communicate with JP sites using the code below and with wpcom sites via reqular wp_query.
Testing
(replace blog_id and domain with your JP sandbox)
(replace ID with your post id)
See the paypal button appear:

CC @lamosty @sirbrillig @jhnstn @retrofox @jsnajdr