2020-11-18 09:10:44 +01:00
< ? php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
2019-11-15 23:26:29 +01:00
* Simple Payments lets users embed a PayPal button fully integrated with wpcom to sell products on the site .
* This is not a proper module yet , because not all the pieces are in place . Until everything is shipped , it can be turned
* into module that can be enabled / disabled .
2020-11-18 09:10:44 +01:00
*
2021-04-27 08:32:47 +02:00
* @ package automattic / jetpack
2020-11-18 09:10:44 +01:00
*/
/**
* Jetpack_Simple_Payments
*/
2019-11-15 23:26:29 +01:00
class Jetpack_Simple_Payments {
// These have to be under 20 chars because that is CPT limit.
2022-06-16 14:01:47 +02:00
/**
* Post type order .
*
* @ var string
*/
public static $post_type_order = 'jp_pay_order' ;
/**
* Post type product .
*
* @ var string
*/
public static $post_type_product = 'jp_pay_product' ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
/**
* Define simple payment shortcode .
*
* @ var string
*/
public static $shortcode = 'simple-payment' ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
/**
* Define simple payment CSS prefix .
*
* @ var string
*/
public static $css_classname_prefix = 'jetpack-simple-payments' ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
/**
* Which plan the user is on .
*
* @ var string value_bundle or jetpack_premium
*/
public static $required_plan ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
/**
* Instance of the class .
*
* @ var Jetpack_Simple_Payments
*/
2019-11-15 23:26:29 +01:00
private static $instance ;
2022-06-16 14:01:47 +02:00
/**
* Construction function .
*/
2019-11-15 23:26:29 +01:00
private function __construct () {}
2022-06-16 14:01:47 +02:00
/**
* Original singleton .
*
* @ todo Remove this when nothing calles getInstance anymore .
*
* @ deprecated 10.8
*/
public static function getInstance () { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
_deprecated_function ( __METHOD__ , 'Jetpack 10.7.0' , 'Jetpack_Simple_Payments::get_instance' );
return self :: get_instance ();
}
/**
* Create instance of class .
*/
public static function get_instance () {
2019-11-15 23:26:29 +01:00
if ( ! self :: $instance ) {
self :: $instance = new self ();
self :: $instance -> register_init_hooks ();
self :: $required_plan = ( defined ( 'IS_WPCOM' ) && IS_WPCOM ) ? 'value_bundle' : 'jetpack_premium' ;
}
return self :: $instance ;
}
2022-06-16 14:01:47 +02:00
/**
* Register scripts and styles .
*/
2019-11-15 23:26:29 +01:00
private function register_scripts_and_styles () {
/**
* Paypal heavily discourages putting that script in your own server :
2022-06-16 14:01:47 +02:00
*
2019-11-15 23:26:29 +01:00
* @ see https :// developer . paypal . com / docs / integration / direct / express - checkout / integration - jsv4 / add - paypal - button /
*/
2023-04-26 17:39:43 +02:00
wp_register_script ( // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Ignored here instead of on the $ver param line since wpcom isn't in sync with ruleset changes in: https://github.com/Automattic/jetpack/pull/28199
2022-06-16 14:01:47 +02:00
'paypal-checkout-js' ,
'https://www.paypalobjects.com/api/checkout.js' ,
array (),
2023-04-26 17:39:43 +02:00
null , // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
2022-06-16 14:01:47 +02:00
true
);
2022-04-02 10:26:41 +02:00
wp_register_script (
'jetpack-paypal-express-checkout' ,
plugins_url ( '/paypal-express-checkout.js' , __FILE__ ),
array ( 'jquery' , 'paypal-checkout-js' ),
2022-06-16 14:01:47 +02:00
JETPACK__VERSION ,
false
);
wp_register_style (
'jetpack-simple-payments' ,
plugins_url ( '/simple-payments.css' , __FILE__ ),
array ( 'dashicons' ),
JETPACK__VERSION ,
2022-04-02 10:26:41 +02:00
false
);
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
/**
* Register init hooks .
*/
2019-11-15 23:26:29 +01:00
private function register_init_hooks () {
add_action ( 'init' , array ( $this , 'init_hook_action' ) );
add_action ( 'rest_api_init' , array ( $this , 'register_meta_fields_in_rest_api' ) );
}
2022-06-16 14:01:47 +02:00
/**
* Register the shortcode .
*/
2019-11-15 23:26:29 +01:00
private function register_shortcode () {
add_shortcode ( self :: $shortcode , array ( $this , 'parse_shortcode' ) );
}
2022-06-16 14:01:47 +02:00
/**
* Actions that are run on init .
*/
2019-11-15 23:26:29 +01:00
public function init_hook_action () {
add_filter ( 'rest_api_allowed_post_types' , array ( $this , 'allow_rest_api_types' ) );
add_filter ( 'jetpack_sync_post_meta_whitelist' , array ( $this , 'allow_sync_post_meta' ) );
if ( ! is_admin () ) {
$this -> register_scripts_and_styles ();
}
$this -> register_shortcode ();
$this -> setup_cpts ();
add_filter ( 'the_content' , array ( $this , 'remove_auto_paragraph_from_product_description' ), 0 );
}
2020-10-20 18:05:12 +02:00
/**
* Enqueue the static assets needed in the frontend .
*/
public function enqueue_frontend_assets () {
if ( ! wp_style_is ( 'jetpack-simple-payments' , 'enqueued' ) ) {
wp_enqueue_style ( 'jetpack-simple-payments' );
2019-11-15 23:26:29 +01:00
}
2020-10-20 18:05:12 +02:00
2022-04-02 10:26:41 +02:00
if ( ! wp_script_is ( 'jetpack-paypal-express-checkout' , 'enqueued' ) ) {
wp_enqueue_script ( 'jetpack-paypal-express-checkout' );
2020-10-20 18:05:12 +02:00
}
}
/**
* Add an inline script for setting up the PayPal checkout button .
*
* @ param int $id Product ID .
* @ param int $dom_id ID of the DOM element with the purchase message .
* @ param boolean $is_multiple Whether multiple items of the same product can be purchased .
*/
public function setup_paypal_checkout_button ( $id , $dom_id , $is_multiple ) {
wp_add_inline_script (
2022-04-02 10:26:41 +02:00
'jetpack-paypal-express-checkout' ,
2020-10-20 18:05:12 +02:00
sprintf (
" try { PaypalExpressCheckout.renderButton( '%d', '%d', '%s', '%d' );}catch(e) { } " ,
esc_js ( $this -> get_blog_id () ),
esc_js ( $id ),
esc_js ( $dom_id ),
esc_js ( $is_multiple )
)
);
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
/**
* Remove auto paragraph from product description .
*
* @ param string $content - the content of the post .
*/
public function remove_auto_paragraph_from_product_description ( $content ) {
2019-11-15 23:26:29 +01:00
if ( get_post_type () === self :: $post_type_product ) {
remove_filter ( 'the_content' , 'wpautop' );
}
return $content ;
}
2022-06-16 14:01:47 +02:00
/** Return the blog ID */
public function get_blog_id () {
2019-11-15 23:26:29 +01:00
if ( defined ( 'IS_WPCOM' ) && IS_WPCOM ) {
return get_current_blog_id ();
}
return Jetpack_Options :: get_option ( 'id' );
}
/**
* Used to check whether Simple Payments are enabled for given site .
*
* @ return bool True if Simple Payments are enabled , false otherwise .
*/
2022-06-16 14:01:47 +02:00
public function is_enabled_jetpack_simple_payments () {
2019-11-15 23:26:29 +01:00
/**
* Can be used by plugin authors to disable the conflicting output of Simple Payments .
*
* @ since 6.3 . 0
*
* @ param bool True if Simple Payments should be disabled , false otherwise .
*/
if ( apply_filters ( 'jetpack_disable_simple_payments' , false ) ) {
return false ;
}
2022-06-16 14:01:47 +02:00
return (
( ( defined ( 'IS_WPCOM' ) && IS_WPCOM ) || Jetpack :: is_connection_ready () ) &&
Jetpack_Plan :: supports ( 'simple-payments' )
);
2019-11-15 23:26:29 +01:00
}
2022-04-02 10:26:41 +02:00
/**
* Get a WP_Post representation of a product
*
* @ param int $id The ID of the product .
*
* @ return array | false | WP_Post
*/
private function get_product ( $id ) {
if ( ! $id ) {
return false ;
2019-11-15 23:26:29 +01:00
}
2022-04-02 10:26:41 +02:00
$product = get_post ( $id );
2019-11-15 23:26:29 +01:00
if ( ! $product || is_wp_error ( $product ) ) {
2022-04-02 10:26:41 +02:00
return false ;
2019-11-15 23:26:29 +01:00
}
if ( $product -> post_type !== self :: $post_type_product || 'publish' !== $product -> post_status ) {
2022-04-02 10:26:41 +02:00
return false ;
}
return $product ;
}
/**
* Creates the content from a shortcode
*
* @ param array $attrs Shortcode attributes .
* @ param mixed $content unused .
*
* @ return string | void
*/
public function parse_shortcode ( $attrs , $content = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
if ( empty ( $attrs [ 'id' ] ) ) {
return ;
}
$product = $this -> get_product ( $attrs [ 'id' ] );
if ( ! $product ) {
2019-11-15 23:26:29 +01:00
return ;
}
2022-06-16 14:01:47 +02:00
// We allow for overriding the presentation labels.
$data = shortcode_atts (
array (
'blog_id' => $this -> get_blog_id (),
'dom_id' => uniqid ( self :: $css_classname_prefix . '-' . $product -> ID . '_' , true ),
'class' => self :: $css_classname_prefix . '-' . $product -> ID ,
'title' => get_the_title ( $product ),
'description' => $product -> post_content ,
'cta' => get_post_meta ( $product -> ID , 'spay_cta' , true ),
'multiple' => get_post_meta ( $product -> ID , 'spay_multiple' , true ) || '0' ,
),
$attrs
);
2019-11-15 23:26:29 +01:00
$data [ 'price' ] = $this -> format_price (
get_post_meta ( $product -> ID , 'spay_price' , true ),
get_post_meta ( $product -> ID , 'spay_currency' , true )
);
$data [ 'id' ] = $attrs [ 'id' ];
if ( ! $this -> is_enabled_jetpack_simple_payments () ) {
2020-11-18 09:10:44 +01:00
if ( jetpack_is_frontend () ) {
2020-05-06 17:20:49 +02:00
return $this -> output_admin_warning ( $data );
2019-11-15 23:26:29 +01:00
}
return ;
}
2020-10-20 18:05:12 +02:00
$this -> enqueue_frontend_assets ();
$this -> setup_paypal_checkout_button ( $attrs [ 'id' ], $data [ 'dom_id' ], $data [ 'multiple' ] );
2019-11-15 23:26:29 +01:00
return $this -> output_shortcode ( $data );
}
2022-06-16 14:01:47 +02:00
/**
* Output an admin warning if user can ' t use Pay with PayPal .
*
* @ param array $data unused .
*/
public function output_admin_warning ( $data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
2019-11-15 23:26:29 +01:00
if ( ! current_user_can ( 'manage_options' ) ) {
return ;
}
2022-12-15 17:41:53 +01:00
require_once JETPACK__PLUGIN_DIR . '_inc/lib/components.php' ;
2022-06-16 14:01:47 +02:00
return Jetpack_Components :: render_upgrade_nudge (
array (
'plan' => self :: $required_plan ,
)
);
2019-11-15 23:26:29 +01:00
}
2020-10-20 18:05:12 +02:00
/**
* Get the HTML output to use as PayPal purchase box .
*
* @ param string $dom_id ID of the DOM element with the purchase message .
* @ param boolean $is_multiple Whether multiple items of the same product can be purchased .
*
* @ return string
*/
public function output_purchase_box ( $dom_id , $is_multiple ) {
2022-06-16 14:01:47 +02:00
$items = '' ;
2019-11-15 23:26:29 +01:00
$css_prefix = self :: $css_classname_prefix ;
2020-10-20 18:05:12 +02:00
if ( $is_multiple ) {
2022-06-16 14:01:47 +02:00
$items = sprintf (
'
2019-11-15 23:26:29 +01:00
< div class = " %1 $s " >
< input class = " %2 $s " type = " number " value = " 1 " min = " 1 " id = " %3 $s " />
</ div >
' ,
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -items " ),
esc_attr ( " { $css_prefix } -items-number " ),
2020-10-20 18:05:12 +02:00
esc_attr ( " { $dom_id } _number " )
2019-11-15 23:26:29 +01:00
);
}
2020-10-20 18:05:12 +02:00
return sprintf (
'<div class="%1$s" id="%2$s"></div><div class="%3$s">%4$s<div class="%5$s" id="%6$s"></div></div>' ,
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -purchase-message " ),
2020-10-20 18:05:12 +02:00
esc_attr ( " { $dom_id } -message-container " ),
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -purchase-box " ),
2020-10-20 18:05:12 +02:00
$items ,
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -button " ),
2020-10-20 18:05:12 +02:00
esc_attr ( " { $dom_id } _button " )
);
}
/**
* Get the HTML output to replace the `simple-payments` shortcode .
*
* @ param array $data Product data .
* @ return string
*/
public function output_shortcode ( $data ) {
$css_prefix = self :: $css_classname_prefix ;
2022-06-16 14:01:47 +02:00
$image = '' ;
if ( has_post_thumbnail ( $data [ 'id' ] ) ) {
$image = sprintf (
'<div class="%1$s"><div class="%2$s">%3$s</div></div>' ,
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -product-image " ),
esc_attr ( " { $css_prefix } -image " ),
2019-11-15 23:26:29 +01:00
get_the_post_thumbnail ( $data [ 'id' ], 'full' )
);
}
2020-10-20 18:05:12 +02:00
2022-06-16 14:01:47 +02:00
return sprintf (
'
2019-11-15 23:26:29 +01:00
< div class = " %1 $s " >
< div class = " %2 $s " >
% 3 $s
< div class = " %4 $s " >
< div class = " %5 $s " >< p >% 6 $s </ p ></ div >
< div class = " %7 $s " >< p >% 8 $s </ p ></ div >
< div class = " %9 $s " >< p >% 10 $s </ p ></ div >
2020-10-20 18:05:12 +02:00
% 11 $s
2019-11-15 23:26:29 +01:00
</ div >
</ div >
</ div >
' ,
2023-01-25 20:43:46 +01:00
esc_attr ( " { $data [ 'class' ] } { $css_prefix } -wrapper " ),
esc_attr ( " { $css_prefix } -product " ),
2019-11-15 23:26:29 +01:00
$image ,
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -details " ),
esc_attr ( " { $css_prefix } -title " ),
2019-11-15 23:26:29 +01:00
esc_html ( $data [ 'title' ] ),
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -description " ),
2019-11-15 23:26:29 +01:00
wp_kses ( $data [ 'description' ], wp_kses_allowed_html ( 'post' ) ),
2023-01-25 20:43:46 +01:00
esc_attr ( " { $css_prefix } -price " ),
2019-11-15 23:26:29 +01:00
esc_html ( $data [ 'price' ] ),
2020-10-20 18:05:12 +02:00
$this -> output_purchase_box ( $data [ 'dom_id' ], $data [ 'multiple' ] )
2019-11-15 23:26:29 +01:00
);
}
/**
* Format a price with currency
*
* Uses currency - aware formatting to output a formatted price with a simple fallback .
*
* Largely inspired by WordPress . com ' s Store_Price :: display_currency
*
* @ param string $price Price .
* @ param string $currency Currency .
* @ return string Formatted price .
*/
private function format_price ( $price , $currency ) {
2022-12-15 17:41:53 +01:00
require_once JETPACK__PLUGIN_DIR . '/_inc/lib/class-jetpack-currencies.php' ;
2020-11-18 09:10:44 +01:00
return Jetpack_Currencies :: format_price ( $price , $currency );
2019-11-15 23:26:29 +01:00
}
/**
* Allows custom post types to be used by REST API .
2022-06-16 14:01:47 +02:00
*
* @ param array $post_types - the allows post types .
2019-11-15 23:26:29 +01:00
* @ see hook 'rest_api_allowed_post_types'
* @ return array
*/
2022-06-16 14:01:47 +02:00
public function allow_rest_api_types ( $post_types ) {
2019-11-15 23:26:29 +01:00
$post_types [] = self :: $post_type_order ;
$post_types [] = self :: $post_type_product ;
return $post_types ;
}
2022-06-16 14:01:47 +02:00
/**
* Merge $post_meta with additional meta information .
*
* @ param array $post_meta - the post ' s meta information .
*/
public function allow_sync_post_meta ( $post_meta ) {
return array_merge (
$post_meta ,
array (
'spay_paypal_id' ,
'spay_status' ,
'spay_product_id' ,
'spay_quantity' ,
'spay_price' ,
'spay_customer_email' ,
'spay_currency' ,
'spay_cta' ,
'spay_email' ,
'spay_multiple' ,
'spay_formatted_price' ,
)
);
2019-11-15 23:26:29 +01:00
}
/**
* Enable Simple payments custom meta values for access through the REST API .
* Field’ s value will be exposed on a . meta key in the endpoint response ,
* and WordPress will handle setting up the callbacks for reading and writing
* to that meta key .
*
* @ link https :// developer . wordpress . org / rest - api / extending - the - rest - api / modifying - responses /
*/
public function register_meta_fields_in_rest_api () {
2022-06-16 14:01:47 +02:00
register_meta (
'post' ,
'spay_price' ,
array (
'description' => esc_html__ ( 'Simple payments; price.' , 'jetpack' ),
'object_subtype' => self :: $post_type_product ,
'sanitize_callback' => array ( $this , 'sanitize_price' ),
'show_in_rest' => true ,
'single' => true ,
'type' => 'number' ,
)
);
register_meta (
'post' ,
'spay_currency' ,
array (
'description' => esc_html__ ( 'Simple payments; currency code.' , 'jetpack' ),
'object_subtype' => self :: $post_type_product ,
'sanitize_callback' => array ( $this , 'sanitize_currency' ),
'show_in_rest' => true ,
'single' => true ,
'type' => 'string' ,
)
);
register_meta (
'post' ,
'spay_cta' ,
array (
'description' => esc_html__ ( 'Simple payments; text with "Buy" or other CTA' , 'jetpack' ),
'object_subtype' => self :: $post_type_product ,
'sanitize_callback' => 'sanitize_text_field' ,
'show_in_rest' => true ,
'single' => true ,
'type' => 'string' ,
)
);
register_meta (
'post' ,
'spay_multiple' ,
array (
'description' => esc_html__ ( 'Simple payments; allow multiple items' , 'jetpack' ),
'object_subtype' => self :: $post_type_product ,
'sanitize_callback' => 'rest_sanitize_boolean' ,
'show_in_rest' => true ,
'single' => true ,
'type' => 'boolean' ,
)
);
register_meta (
'post' ,
'spay_email' ,
array (
'description' => esc_html__ ( 'Simple payments button; paypal email.' , 'jetpack' ),
2022-12-15 17:41:53 +01:00
'object_subtype' => self :: $post_type_product ,
2022-06-16 14:01:47 +02:00
'sanitize_callback' => 'sanitize_email' ,
'show_in_rest' => true ,
'single' => true ,
'type' => 'string' ,
)
);
register_meta (
'post' ,
'spay_status' ,
array (
'description' => esc_html__ ( 'Simple payments; status.' , 'jetpack' ),
'object_subtype' => self :: $post_type_product ,
'sanitize_callback' => 'sanitize_text_field' ,
'show_in_rest' => true ,
'single' => true ,
'type' => 'string' ,
)
);
2019-11-15 23:26:29 +01:00
}
/**
* Sanitize three - character ISO - 4217 Simple payments currency
*
* List has to be in sync with list at the block 's client side and widget' s backend side :
2022-06-16 14:01:47 +02:00
*
* @ param array $currency - list of currencies .
2019-11-15 23:26:29 +01:00
* @ link https :// github . com / Automattic / jetpack / blob / 31 efa189ad223c0eb7ad085ac0650a23facf9ef5 / extensions / blocks / simple - payments / constants . js #L9-L39
* @ link https :// github . com / Automattic / jetpack / blob / 31 efa189ad223c0eb7ad085ac0650a23facf9ef5 / modules / widgets / simple - payments . php #L19-L44
*
* Currencies should be supported by PayPal :
* @ link https :// developer . paypal . com / docs / api / reference / currency - codes /
*
* Indian Rupee ( INR ) not supported because at the time of the creation of this file
* because it ' s limited to in - country PayPal India accounts only .
* Discussion : https :// github . com / Automattic / wp - calypso / pull / 28236
*/
public static function sanitize_currency ( $currency ) {
$valid_currencies = array (
'USD' ,
'EUR' ,
'AUD' ,
'BRL' ,
'CAD' ,
'CZK' ,
'DKK' ,
'HKD' ,
'HUF' ,
'ILS' ,
'JPY' ,
'MYR' ,
'MXN' ,
'TWD' ,
'NZD' ,
'NOK' ,
'PHP' ,
'PLN' ,
'GBP' ,
'RUB' ,
'SGD' ,
'SEK' ,
'CHF' ,
'THB' ,
);
2022-06-16 14:01:47 +02:00
return in_array ( $currency , $valid_currencies , true ) ? $currency : false ;
2019-11-15 23:26:29 +01:00
}
/**
* Sanitize price :
*
* Positive integers and floats
* Supports two decimal places .
* Maximum length : 10.
*
* See `price` from PayPal docs :
2022-06-16 14:01:47 +02:00
*
2019-11-15 23:26:29 +01:00
* @ link https :// developer . paypal . com / docs / api / orders / v1 / #definition-item
*
2022-06-16 14:01:47 +02:00
* @ param string $price - the price we want to sanitize .
2019-11-15 23:26:29 +01:00
* @ return null | string
*/
public static function sanitize_price ( $price ) {
return preg_match ( '/^[0-9]{0,10}(\.[0-9]{0,2})?$/' , $price ) ? $price : false ;
}
/**
* Sets up the custom post types for the module .
*/
2022-06-16 14:01:47 +02:00
public function setup_cpts () {
2019-11-15 23:26:29 +01:00
/*
* ORDER data structure . holds :
* title = customer_name | 4 xproduct_name
* excerpt = customer_name + customer contact info + customer notes from paypal form
* metadata :
* spay_paypal_id - paypal id of transaction
* spay_status
* spay_product_id - post_id of bought product
* spay_quantity - quantity of product
* spay_price - item price at the time of purchase
* spay_customer_email - customer email
* ... ( WIP )
*/
$order_capabilities = array (
2022-06-16 14:01:47 +02:00
'edit_post' => 'edit_posts' ,
'read_post' => 'read_private_posts' ,
'delete_post' => 'delete_posts' ,
'edit_posts' => 'edit_posts' ,
'edit_others_posts' => 'edit_others_posts' ,
'publish_posts' => 'publish_posts' ,
'read_private_posts' => 'read_private_posts' ,
2019-11-15 23:26:29 +01:00
);
2022-06-16 14:01:47 +02:00
$order_args = array (
'label' => esc_html_x ( 'Order' , 'noun: a quantity of goods or items purchased or sold' , 'jetpack' ),
'description' => esc_html__ ( 'Simple Payments orders' , 'jetpack' ),
'supports' => array ( 'custom-fields' , 'excerpt' ),
'hierarchical' => false ,
'public' => false ,
'show_ui' => false ,
'show_in_menu' => false ,
'show_in_admin_bar' => false ,
'show_in_nav_menus' => false ,
'can_export' => true ,
'has_archive' => false ,
'exclude_from_search' => true ,
'publicly_queryable' => false ,
'rewrite' => false ,
'capabilities' => $order_capabilities ,
'show_in_rest' => true ,
2019-11-15 23:26:29 +01:00
);
register_post_type ( self :: $post_type_order , $order_args );
/*
* PRODUCT data structure . Holds :
* title - title
* content - description
* thumbnail - image
* metadata :
* spay_price - price
* spay_formatted_price
* spay_currency - currency code
* spay_cta - text with " Buy " or other CTA
* spay_email - paypal email
* spay_multiple - allow for multiple items
* spay_status - status . { enabled | disabled }
*/
$product_capabilities = array (
2022-06-16 14:01:47 +02:00
'edit_post' => 'edit_posts' ,
'read_post' => 'read_private_posts' ,
'delete_post' => 'delete_posts' ,
'edit_posts' => 'publish_posts' ,
'edit_others_posts' => 'edit_others_posts' ,
'publish_posts' => 'publish_posts' ,
'read_private_posts' => 'read_private_posts' ,
2019-11-15 23:26:29 +01:00
);
2022-06-16 14:01:47 +02:00
$product_args = array (
'label' => esc_html__ ( 'Product' , 'jetpack' ),
'description' => esc_html__ ( 'Simple Payments products' , 'jetpack' ),
'supports' => array ( 'title' , 'editor' , 'thumbnail' , 'custom-fields' , 'author' ),
'hierarchical' => false ,
'public' => false ,
'show_ui' => false ,
'show_in_menu' => false ,
'show_in_admin_bar' => false ,
'show_in_nav_menus' => false ,
'can_export' => true ,
'has_archive' => false ,
'exclude_from_search' => true ,
'publicly_queryable' => false ,
'rewrite' => false ,
'capabilities' => $product_capabilities ,
'show_in_rest' => true ,
2019-11-15 23:26:29 +01:00
);
register_post_type ( self :: $post_type_product , $product_args );
}
2022-04-02 10:26:41 +02:00
/**
* Validate the block attributes
*
* @ param array $attrs The block attributes , expected to contain :
* * email - an email address .
* * price - a float between 0.01 and 9999999999.99 .
* * productId - the ID of the product being paid for .
*
* @ return bool
*/
public function is_valid ( $attrs ) {
if ( ! $this -> validate_paypal_email ( $attrs ) ) {
return false ;
}
if ( ! $this -> validate_price ( $attrs ) ) {
return false ;
}
if ( ! $this -> validate_product ( $attrs ) ) {
return false ;
}
return true ;
}
/**
* Check that the email address to make a payment to is valid
*
* @ param array $attrs Key - value array of attributes .
*
* @ return boolean
*/
private function validate_paypal_email ( $attrs ) {
if ( empty ( $attrs [ 'email' ] ) ) {
return false ;
}
return ( bool ) filter_var ( $attrs [ 'email' ], FILTER_VALIDATE_EMAIL );
}
/**
* Check that the price is valid
*
* @ param array $attrs Key - value array of attributes .
*
* @ return bool
*/
private function validate_price ( $attrs ) {
if ( empty ( $attrs [ 'price' ] ) ) {
return false ;
}
return ( bool ) self :: sanitize_price ( $attrs [ 'price' ] );
}
/**
* Check that the stored product is valid
*
* Valid means it has a title , and the currency is accepted .
*
* @ param array $attrs Key - value array of attributes .
*
* @ return bool
*/
private function validate_product ( $attrs ) {
if ( empty ( $attrs [ 'productId' ] ) ) {
return false ;
}
$product = $this -> get_product ( $attrs [ 'productId' ] );
if ( ! $product ) {
return false ;
}
// This title is the one used by paypal, it's set from the title set in the block content, unless the block
// content title is blank.
if ( ! get_the_title ( $product ) ) {
return false ;
}
$currency = get_post_meta ( $product -> ID , 'spay_currency' , true );
return ( bool ) self :: sanitize_currency ( $currency );
}
2019-11-15 23:26:29 +01:00
/**
* Format a price for display
*
* Largely taken from WordPress . com Store_Price class
*
* The currency array will have the shape :
* format => string sprintf format with placeholders `%1$s` : Symbol `%2$s` : Price .
* symbol => string Symbol string
* desc => string Text description of currency
* decimal => int Number of decimal places
*
* @ param string $the_currency The desired currency , e . g . 'USD' .
* @ return ? array Currency object or null if not found .
*/
private static function get_currency ( $the_currency ) {
2022-12-15 17:41:53 +01:00
require_once JETPACK__PLUGIN_DIR . '/_inc/lib/class-jetpack-currencies.php' ;
2020-11-18 09:10:44 +01:00
$currencies = Jetpack_Currencies :: CURRENCIES ;
2019-11-15 23:26:29 +01:00
if ( isset ( $currencies [ $the_currency ] ) ) {
return $currencies [ $the_currency ];
}
return null ;
}
}
2022-06-16 14:01:47 +02:00
Jetpack_Simple_Payments :: get_instance ();