Payment Token Data Store

This commit is contained in:
Justin Shreve 2016-11-14 06:20:41 -08:00
parent b0f859f6a2
commit 370166a6b0
15 changed files with 657 additions and 302 deletions

View File

@ -209,12 +209,13 @@ abstract class WC_Data {
/**
* Get Meta Data by Key.
* @since 2.6.0
* @since 2.6.0
* @param string $key
* @param bool $single return first found meta with key, or all with $key
* @param string $context What the value is for. Valid values are view and edit.
* @return mixed
*/
public function get_meta( $key = '', $single = true ) {
public function get_meta( $key = '', $single = true, $context = 'view' ) {
$array_keys = array_keys( wp_list_pluck( $this->get_meta_data(), 'key' ), $key );
$value = '';
@ -224,6 +225,10 @@ abstract class WC_Data {
} else {
$value = array_intersect_key( $this->meta_data, array_flip( $array_keys ) );
}
if ( 'view' === $context ) {
$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
}
}
return $value;
@ -524,7 +529,7 @@ abstract class WC_Data {
*
* @since 2.7.0
*/
protected function apply_changes() {
public function apply_changes() {
$this->data = array_merge( $this->data, $this->changes );
$this->changes = array();
}

View File

@ -0,0 +1,56 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Legacy Payment Tokens.
* Payment Tokens were introduced in 2.6.0 with create and update as methods.
* Major CRUD changes occurred in 2.7, so these were deprecated (save and delete still work).
* This legacy class is for backwards compatibility in case any code called ->read, ->update or ->create
* directly on the object.
*
* @version 2.7.0
* @package WooCommerce/Classes
* @category Class
* @author WooCommerce
*/
abstract class WC_Legacy_Payment_Token extends WC_Data {
/**
* Read a tokem by ID (deprecated).
*/
public function read( $token_id ) {
_deprecated_function( 'WC_Payment_Token::read', '2.7', 'Init a token class with an ID.' );
$this->set_id( $token_id );
$data_store = WC_Data_Store::load( 'payment-token' );
$data_store->read( $this );
}
/**
* Update a token (deprecated).
*/
public function update() {
_deprecated_function( 'WC_Payment_Token::update', '2.7', 'Use ::save instead.' );
$data_store = WC_Data_Store::load( 'payment-token' );
try {
$data_store->update( $this );
} catch ( Exception $e ) {
return false;
}
}
/**
* Create a token (deprecated).
*/
public function create() {
_deprecated_function( 'WC_Payment_Token::create', '2.7', 'Use ::save instead.' );
$data_store = WC_Data_Store::load( 'payment-token' );
try {
$data_store->create( $this );
} catch ( Exception $e ) {
return false;
}
}
}

View File

@ -1,6 +1,8 @@
<?php
include_once( 'abstract-wc-legacy-payment-token.php' );
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
/**
@ -10,12 +12,13 @@ if ( ! defined( 'ABSPATH' ) ) {
* examples: Credit Card, eCheck.
*
* @class WC_Payment_Token
* @version 2.7.0
* @since 2.6.0
* @package WooCommerce/Abstracts
* @category Abstract Class
* @author WooThemes
*/
abstract class WC_Payment_Token extends WC_Data {
abstract class WC_Payment_Token extends WC_Legacy_Payment_Token {
/**
* Token Data (stored in the payment_tokens table).
@ -24,8 +27,9 @@ if ( ! defined( 'ABSPATH' ) ) {
protected $data = array(
'gateway_id' => '',
'token' => '',
'is_default' => 0,
'is_default' => false,
'user_id' => 0,
'type' => '',
);
/**
@ -34,111 +38,158 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
protected $meta_type = 'payment_token';
/**
* Initialize a payment token.
*
* These fields are accepted by all payment tokens:
* is_default - boolean Optional - Indicates this is the default payment token for a user
* token - string Required - The actual token to store
* gateway_id - string Required - Identifier for the gateway this token is associated with
* user_id - int Optional - ID for the user this token is associated with. 0 if this token is not associated with a user
*
* @since 2.6.0
* @param mixed $token
*/
/**
* Initialize a payment token.
*
* These fields are accepted by all payment tokens:
* is_default - boolean Optional - Indicates this is the default payment token for a user
* token - string Required - The actual token to store
* gateway_id - string Required - Identifier for the gateway this token is associated with
* user_id - int Optional - ID for the user this token is associated with. 0 if this token is not associated with a user
*
* @since 2.6.0
* @param mixed $token
*/
public function __construct( $token = '' ) {
if ( is_numeric( $token ) ) {
$this->read( $token );
} elseif ( is_object( $token ) ) {
$token_id = $token->get_id();
if ( ! empty( $token_id ) ) {
$this->read( $token->get_id() );
}
}
// Set token type (cc, echeck)
if ( ! empty( $this->type ) ) {
$this->data['type'] = $this->type;
}
if ( is_numeric( $token ) ) {
$this->set_id( $token );
} elseif ( is_object( $token ) ) {
$token_id = $token->get_id();
if ( ! empty( $token_id ) ) {
$this->set_id( $token->get_id() );
}
} else {
$this->set_object_read( true );
}
$this->data_store = WC_Data_Store::load( 'payment-token' );
if ( $this->get_id() > 0 ) {
$this->data_store->read( $this );
}
}
/*
|--------------------------------------------------------------------------
| Getters
|--------------------------------------------------------------------------
*/
/**
* Returns the raw payment token.
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Raw token
*/
public function get_token() {
return $this->data['token'];
}
/**
* Set the raw payment token.
* @since 2.6.0
* @param string $token
*/
public function set_token( $token ) {
$this->data['token'] = $token;
public function get_token( $context = 'view' ) {
return $this->get_prop( 'token', $context );
}
/**
* Returns the type of this payment token (CC, eCheck, or something else).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Payment Token Type (CC, eCheck)
*/
public function get_type() {
return isset( $this->data['type'] ) ? $this->data['type'] : '';
public function get_type( $context = 'view' ) {
return $this->get_prop( 'type', $context );
}
/**
* Get type to display to user.
* Get's overwritten by child classes.
*
* @since 2.6.0
* @param string $context
* @return string
*/
public function get_display_name() {
return $this->get_type();
public function get_display_name( $context = 'view' ) {
return $this->get_type( $context );
}
/**
* Returns the user ID associated with the token or false if this token is not associated.
*
* @since 2.6.0
* @param string $context
* @return int User ID if this token is associated with a user or 0 if no user is associated
*/
public function get_user_id() {
return ( isset( $this->data['user_id'] ) && $this->data['user_id'] > 0 ) ? absint( $this->data['user_id'] ) : 0;
}
/**
* Set the user ID for the user associated with this order.
* @since 2.6.0
* @param int $user_id
*/
public function set_user_id( $user_id ) {
$this->data['user_id'] = absint( $user_id );
public function get_user_id( $context = 'view' ) {
return $this->get_prop( 'user_id', $context );
}
/**
* Returns the ID of the gateway associated with this payment token.
*
* @since 2.6.0
* @param string $context
* @return string Gateway ID
*/
public function get_gateway_id() {
return $this->data['gateway_id'];
public function get_gateway_id( $context = 'view' ) {
return $this->get_prop( 'gateway_id', $context );
}
/**
* Returns the ID of the gateway associated with this payment token.
*
* @since 2.6.0
* @param string $context
* @return string Gateway ID
*/
public function get_is_default( $context = 'view' ) {
return $this->get_prop( 'is_default', $context );
}
/*
|--------------------------------------------------------------------------
| Setters
|--------------------------------------------------------------------------
*/
/**
* Set the raw payment token.
*
* @since 2.6.0
* @param string $token
*/
public function set_token( $token ) {
$this->set_prop( 'token', $token );
}
/**
* Sets the type of this payment token (CC, eCheck, or something else).
*
* @since 2.7.0
* @param string Payment Token Type (CC, eCheck)
*/
public function set_type( $type ) {
return $this->get_prop( 'type', $type );
}
/**
* Set the user ID for the user associated with this order.
*
* @since 2.6.0
* @param int $user_id
*/
public function set_user_id( $user_id ) {
$this->set_prop( 'user_id', absint( $user_id ) );
}
/**
* Set the gateway ID.
*
* @since 2.6.0
* @param string $gateway_id
*/
public function set_gateway_id( $gateway_id ) {
$this->data['gateway_id'] = $gateway_id;
}
/**
* Returns if the token is marked as default.
* @since 2.6.0
* @return boolean True if the token is default
*/
public function is_default() {
return ! empty( $this->data['is_default'] );
$this->set_prop( 'gateway_id', $gateway_id);
}
/**
@ -147,7 +198,23 @@ if ( ! defined( 'ABSPATH' ) ) {
* @param boolean $is_default True or false
*/
public function set_default( $is_default ) {
$this->data['is_default'] = (bool) $is_default;
$this->set_prop( 'is_default', (bool) $is_default );
}
/*
|--------------------------------------------------------------------------
| Other Methods
|--------------------------------------------------------------------------
*/
/**
* Returns if the token is marked as default.
*
* @since 2.6.0
* @return boolean True if the token is default
*/
public function is_default() {
return (bool) $this->get_prop( 'is_default', 'view' );
}
/**
@ -156,132 +223,17 @@ if ( ! defined( 'ABSPATH' ) ) {
* @return boolean True if the passed data is valid
*/
public function validate() {
if ( empty( $this->data['token'] ) ) {
$token = $this->get_prop( 'token', 'edit' );
if ( empty( $token ) ) {
return false;
}
if ( empty( $this->data['type'] ) ) {
$type = $this->get_prop( 'type', 'edit' );
if ( empty( $type ) ) {
return false;
}
return true;
}
/**
* Get a token from the database.
* @since 2.6.0
* @param int $token_id Token ID
*/
public function read( $token_id ) {
global $wpdb;
if ( $token = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d LIMIT 1;", $token_id ) ) ) {
$token_id = $token->token_id;
$token = (array) $token;
unset( $token['token_id'] );
$this->data = $token;
$this->set_id( $token_id );
$this->read_meta_data();
}
}
/**
* Update a payment token.
* @since 2.6.0
* @return boolean on success, false if validation failed and a payment token could not be updated
*/
public function update() {
if ( false === $this->validate() ) {
return false;
}
global $wpdb;
$payment_token_data = array(
'gateway_id' => $this->get_gateway_id(),
'token' => $this->get_token(),
'user_id' => $this->get_user_id(),
'type' => $this->get_type(),
);
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
$payment_token_data,
array( 'token_id' => $this->get_id() )
);
$this->save_meta_data();
// Make sure all other tokens are not set to default
if ( $this->is_default() && $this->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $this->get_user_id(), $this->get_id() );
}
do_action( 'woocommerce_payment_token_updated', $this->get_id() );
return true;
}
/**
* Create a new payment token in the database.
* @since 2.6.0
* @return boolean on success, false if validation failed and a payment token could not be created
*/
public function create() {
if ( false === $this->validate() ) {
return false;
}
global $wpdb;
// Are there any other tokens? If not, set this token as default
if ( ! $this->is_default() && $this->get_user_id() > 0 ) {
$default_token = WC_Payment_Tokens::get_customer_default_token( $this->get_user_id() );
if ( is_null( $default_token ) ) {
$this->set_default( true );
}
}
$payment_token_data = array(
'gateway_id' => $this->get_gateway_id(),
'token' => $this->get_token(),
'user_id' => $this->get_user_id(),
'type' => $this->get_type(),
);
$wpdb->insert( $wpdb->prefix . 'woocommerce_payment_tokens', $payment_token_data );
$token_id = $wpdb->insert_id;
$this->set_id( $token_id );
$this->save_meta_data();
// Make sure all other tokens are not set to default
if ( $this->is_default() && $this->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $this->get_user_id(), $token_id );
}
do_action( 'woocommerce_payment_token_created', $token_id );
return true;
}
/**
* Saves a payment token to the database - does not require you to know if this is a new token or an update token.
* @since 2.6.0
* @return boolean on success, false if validation failed and a payment token could not be saved
*/
public function save() {
if ( $this->get_id() > 0 ) {
return $this->update();
} else {
return $this->create();
}
}
/**
* Remove a payment token from the database.
* @since 2.6.0
*/
public function delete( $force_delete = false ) {
global $wpdb;
$this->read( $this->get_id() ); // Make sure we have a token to return after deletion
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokens', array( 'token_id' => $this->get_id() ), array( '%d' ) );
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokenmeta', array( 'payment_token_id' => $this->get_id() ), array( '%d' ) );
do_action( 'woocommerce_payment_token_deleted', $this->get_id(), $this );
}
}

View File

@ -28,7 +28,8 @@ class WC_Data_Store {
* Ran through `woocommerce_data_stores`.
*/
private $stores = array(
'coupon' => 'WC_Coupon_Data_Store_CPT',
'coupon' => 'WC_Coupon_Data_Store_CPT',
'payment-token' => 'WC_Payment_Token_Data_Store_Table',
);
/**
* Contains the name of the current data store's class name.

View File

@ -9,6 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* An API for storing and managing tokens for gateways and customers.
*
* @class WC_Payment_Tokens
* @version 2.7.0
* @since 2.6.0
* @package WooCommerce/Classes
* @category Class
@ -18,12 +19,12 @@ class WC_Payment_Tokens {
/**
* Gets valid tokens from the database based on user defined criteria.
*
* @since 2.6.0
* @param array $args
* @return array
*/
public static function get_tokens( $args ) {
global $wpdb;
$args = wp_parse_args( $args, array(
'token_id' => '',
'user_id' => '',
@ -31,33 +32,8 @@ class WC_Payment_Tokens {
'type' => '',
) );
$sql = "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens";
$where = array( '1=1' );
if ( $args['token_id'] ) {
$token_ids = array_map( 'absint', is_array( $args['token_id'] ) ? $args['token_id'] : array( $args['token_id'] ) );
$where[] = "token_id IN ('" . implode( "','", array_map( 'esc_sql', $token_ids ) ) . "')";
}
if ( $args['user_id'] ) {
$where[] = 'user_id = ' . absint( $args['user_id'] );
}
if ( $args['gateway_id'] ) {
$gateway_ids = array( $args['gateway_id'] );
} else {
$gateways = WC_Payment_Gateways::instance();
$gateway_ids = $gateways->get_payment_gateway_ids();
}
$gateway_ids[] = '';
$where[] = "gateway_id IN ('" . implode( "','", array_map( 'esc_sql', $gateway_ids ) ) . "')";
if ( $args['type'] ) {
$where[] = 'type = ' . esc_sql( $args['type'] );
}
$token_results = $wpdb->get_results( $sql . ' WHERE ' . implode( ' AND ', $where ) );
$data_store = WC_Data_Store::load( 'payment-token' );
$token_results = $data_store->get_tokens( $args );
$tokens = array();
if ( ! empty( $token_results ) ) {
@ -74,6 +50,7 @@ class WC_Payment_Tokens {
/**
* Returns an array of payment token objects associated with the passed customer ID.
*
* @since 2.6.0
* @param int $customer_id Customer ID
* @param string $gateway_id Optional Gateway ID for getting tokens for a specific gateway
@ -94,6 +71,7 @@ class WC_Payment_Tokens {
/**
* Returns a customers default token or NULL if there is no default token.
*
* @since 2.6.0
* @param int $customer_id
* @return WC_Payment_Token|null
@ -103,12 +81,8 @@ class WC_Payment_Tokens {
return null;
}
global $wpdb;
$token = $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE user_id = %d AND is_default = 1",
$customer_id
) );
$data_store = WC_Data_Store::load( 'payment-token' );
$token = $data_store->get_users_default_token( $customer_id );
if ( $token ) {
return self::get( $token->token_id, $token );
@ -119,6 +93,7 @@ class WC_Payment_Tokens {
/**
* Returns an array of payment token objects associated with the passed order ID.
*
* @since 2.6.0
* @param int $order_id Order ID
* @return array Array of token objects
@ -130,6 +105,7 @@ class WC_Payment_Tokens {
return array();
}
// @todo Order Data Store should handle this one.
$token_ids = get_post_meta( $order_id, '_payment_tokens', true );
if ( empty( $token_ids ) ) {
return array();
@ -144,19 +120,16 @@ class WC_Payment_Tokens {
/**
* Get a token object by ID.
*
* @since 2.6.0
* @param int $token_id Token ID
* @return WC_Payment_Token|null Returns a valid payment token or null if no token can be found
*/
public static function get( $token_id, $token_result = null ) {
global $wpdb;
$data_store = WC_Data_Store::load( 'payment-token' );
if ( is_null( $token_result ) ) {
$token_result = $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
$token_result = $data_store->get_token_by_id( $token_id );
// Still empty? Token doesn't exist? Don't continue
if ( empty( $token_result ) ) {
return null;
@ -166,7 +139,7 @@ class WC_Payment_Tokens {
$token_class = 'WC_Payment_Token_' . $token_result->type;
if ( class_exists( $token_class ) ) {
$meta = get_metadata( 'payment_token', $token_id );
$meta = $data_store->get_metadata( $token_id );
$passed_meta = array();
if ( ! empty( $meta ) ) {
foreach ( $meta as $meta_key => $meta_value ) {
@ -195,50 +168,33 @@ class WC_Payment_Tokens {
/**
* Loops through all of a users payment tokens and sets is_default to false for all but a specific token.
*
* @since 2.6.0
* @param int $user_id User to set a default for
* @param int $token_id The ID of the token that should be default
*/
public static function set_users_default( $user_id, $token_id ) {
global $wpdb; // DB queries so we avoid an infinite loop (update & create use this function)
$data_store = WC_Data_Store::load( 'payment-token' );
$users_tokens = self::get_customer_tokens( $user_id );
foreach ( $users_tokens as $token ) {
if ( $token_id === $token->get_id() ) {
$token->set_default( true );
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
array( 'is_default' => 1 ),
array(
'token_id' => $token->get_id(),
)
);
$data_store->set_default_status( $token->get_id(), true );
do_action( 'woocommerce_payment_token_set_default', $token_id, $token );
} else {
$token->set_default( false );
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
array( 'is_default' => 0 ),
array(
'token_id' => $token->get_id(),
)
);
$data_store->set_default_status( $token->get_id(), false );
}
}
}
/**
* Returns what type (credit card, echeck, etc) of token a token is by ID.
*
* @since 2.6.0
* @param int $token_id Token ID
* @return string Type
*/
public static function get_token_type_by_id( $token_id ) {
global $wpdb;
$type = $wpdb->get_var( $wpdb->prepare(
"SELECT type FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
return $type;
$data_store = WC_Data_Store::load( 'payment-token' );
return $data_store->get_token_type_by_id( $token_id );
}
}

View File

@ -51,7 +51,6 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Da
if ( ! $coupon->get_id() || ! ( $post_object = get_post( $coupon->get_id() ) ) ) {
throw new Exception( __( 'Invalid coupon.', 'woocommerce' ) );
return;
}
$coupon_id = $coupon->get_id();

View File

@ -0,0 +1,256 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC Payment Token Data Store: Custom Table.
*
* @version 2.7.0
* @category Class
* @author WooThemes
*/
class WC_Payment_Token_Data_Store_Table implements WC_Payment_Token_Data_Store, WC_Object_Data_Store {
/**
* Create a new payment token in the database.
*
* @since 2.7.0
* @param WC_Payment_Token $token
*/
public function create( &$token ) {
if ( false === $token->validate() ) {
throw new Exception( __( 'Invalid or missing payment token fields.', 'woocommerce' ) );
}
global $wpdb;
if ( ! $token->is_default() && $token->get_user_id() > 0 ) {
$default_token = WC_Payment_Tokens::get_customer_default_token( $token->get_user_id() );
if ( is_null( $default_token ) ) {
$token->set_default( true );
}
}
$payment_token_data = array(
'gateway_id' => $token->get_gateway_id( 'edit' ),
'token' => $token->get_token( 'edit' ),
'user_id' => $token->get_user_id( 'edit' ),
'type' => $token->get_type( 'edit' ),
);
$wpdb->insert( $wpdb->prefix . 'woocommerce_payment_tokens', $payment_token_data );
$token_id = $wpdb->insert_id;
$token->set_id( $token_id );
$token->save_meta_data();
$token->apply_changes();
// Make sure all other tokens are not set to default
if ( $token->is_default() && $token->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $token->get_user_id(), $token_id );
}
do_action( 'woocommerce_payment_token_created', $token_id );
}
/**
* Update a payment token.
*
* @since 2.7.0
* @param WC_Payment_Token $token
*/
public function update( &$token ) {
if ( false === $token->validate() ) {
throw new Exception( __( 'Invalid or missing payment token fields.', 'woocommerce' ) );
}
global $wpdb;
$payment_token_data = array(
'gateway_id' => $token->get_gateway_id( 'edit' ),
'token' => $token->get_token( 'edit' ),
'user_id' => $token->get_user_id( 'edit' ),
'type' => $token->get_type( 'edit' ),
);
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
$payment_token_data,
array( 'token_id' => $token->get_id( 'edit' ) )
);
$token->save_meta_data();
$token->apply_changes();
// Make sure all other tokens are not set to default
if ( $token->is_default() && $token->get_user_id() > 0 ) {
WC_Payment_Tokens::set_users_default( $token->get_user_id(), $token->get_id() );
}
do_action( 'woocommerce_payment_token_updated', $token->get_id() );
}
/**
* Remove a payment token from the database.
*
* @since 2.7.0
* @param WC_Payment_Token $token
* @param bool $force_delete
*/
public function delete( &$token, $force_delete = false ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokens', array( 'token_id' => $token->get_id() ), array( '%d' ) );
$wpdb->delete( $wpdb->prefix . 'woocommerce_payment_tokenmeta', array( 'payment_token_id' => $token->get_id() ), array( '%d' ) );
do_action( 'woocommerce_payment_token_deleted', $token->get_id(), $token );
}
/**
* Read a token from the database.
*
* @since 2.7.0
* @param WC_Payment_Token $token
*/
public function read( &$token ) {
global $wpdb;
if ( $data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d LIMIT 1;", $token->get_id() ) ) ) {
$token->set_props( array(
'token' => $data->token,
'user_id' => $data->user_id,
'gateway_id' => $data->gateway_id,
'default' => $data->is_default,
) );
$token->read_meta_data();
$token->set_object_read( true );
do_action( 'woocommerce_payment_token_loaded', $token );
} else {
throw new Exception( __( 'Invalid payment token.', 'woocommerce' ) );
}
}
/**
* Returns an array of objects (stdObject) matching specific token critera.
* Accepts token_id, user_id, gateway_id, and type.
* Each object should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 2.7.0
* @param array $args
* @return array
*/
public function get_tokens( $args ) {
global $wpdb;
$args = wp_parse_args( $args, array(
'token_id' => '',
'user_id' => '',
'gateway_id' => '',
'type' => '',
) );
$sql = "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens";
$where = array( '1=1' );
if ( $args['token_id'] ) {
$token_ids = array_map( 'absint', is_array( $args['token_id'] ) ? $args['token_id'] : array( $args['token_id'] ) );
$where[] = "token_id IN ('" . implode( "','", array_map( 'esc_sql', $token_ids ) ) . "')";
}
if ( $args['user_id'] ) {
$where[] = 'user_id = ' . absint( $args['user_id'] );
}
if ( $args['gateway_id'] ) {
$gateway_ids = array( $args['gateway_id'] );
} else {
$gateways = WC_Payment_Gateways::instance();
$gateway_ids = $gateways->get_payment_gateway_ids();
}
$gateway_ids[] = '';
$where[] = "gateway_id IN ('" . implode( "','", array_map( 'esc_sql', $gateway_ids ) ) . "')";
if ( $args['type'] ) {
$where[] = 'type = ' . esc_sql( $args['type'] );
}
$token_results = $wpdb->get_results( $sql . ' WHERE ' . implode( ' AND ', $where ) );
return $token_results;
}
/**
* Returns an stdObject of a token for a user's default token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 2.7.0
* @param id $user_id
* @return object
*/
public function get_users_default_token( $user_id ) {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE user_id = %d AND is_default = 1",
$user_id
) );
}
/**
* Returns an stdObject of a token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
*
* @since 2.7.0
* @param id $token_id
* @return object
*/
public function get_token_by_id( $token_id ) {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
}
/**
* Returns metadata for a specific payment token.
*
* @since 2.7.0
* @param id $token_id
* @return array
*/
public function get_metadata( $token_id ) {
return get_metadata( 'payment_token', $token_id );
}
/**
* Get a token's type by ID.
*
* @since 2.7.0
* @param id $token_id
* @return string
*/
public function get_token_type_by_id( $token_id ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare(
"SELECT type FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id = %d",
$token_id
) );
}
/**
* Update's a tokens default status in the database. Used for quickly
* looping through tokens and setting their statuses instead of creating a bunch
* of objects.
*
* @since 2.7.0
* @param id $token_id
* @return string
*/
public function set_default_status( $token_id, $status = true ) {
global $wpdb;
$wpdb->update(
$wpdb->prefix . 'woocommerce_payment_tokens',
array( 'is_default' => $status ),
array(
'token_id' => $token_id,
)
);
}
}

View File

@ -0,0 +1,65 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC Payment Token Data Store Interface
*
* Functions that must be defined by payment token store classes.
*
* @version 2.7.0
* @category Interface
* @author WooThemes
*/
interface WC_Payment_Token_Data_Store {
/**
* Returns an array of objects (stdObject) matching specific token critera.
* Accepts token_id, user_id, gateway_id, and type.
* Each object should contain the fields token_id, gateway_id, token, user_id, type, is_default.
* @param array $args
* @return array
*/
public function get_tokens( $args );
/**
* Returns an stdObject of a token for a user's default token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
* @param id $user_id
* @return object
*/
public function get_users_default_token( $user_id );
/**
* Returns an stdObject of a token.
* Should contain the fields token_id, gateway_id, token, user_id, type, is_default.
* @param id $token_id
* @return object
*/
public function get_token_by_id( $token_id );
/**
* Returns metadata for a specific payment token.
* @param id $token_id
* @return array
*/
public function get_metadata( $token_id );
/**
* Get a token's type by ID.
*
* @since 2.7.0
* @param id $token_id
* @return string
*/
public function get_token_type_by_id( $token_id );
/**
* Update's a tokens default status in the database. Used for quickly
* looping through tokens and setting their statuses instead of creating a bunch
* of objects.
* @param id $token_id
* @return string
*/
public function set_default_status( $token_id, $status = true );
}

View File

@ -9,6 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* Representation of a payment token for credit cards.
*
* @class WC_Payment_Token_CC
* @version 2.7.0
* @since 2.6.0
* @category PaymentTokens
* @package WooCommerce/PaymentTokens
@ -19,6 +20,15 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/** @protected string Token Type String. */
protected $type = 'CC';
/**
* Hook prefix
*
* @since 2.7.0
*/
protected function get_hook_prefix() {
return 'woocommerce_payment_token_cc_get_';
}
/**
* Validate credit card payment tokens.
*
@ -36,27 +46,27 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
return false;
}
if ( ! $this->get_last4() ) {
if ( ! $this->get_last4( 'edit' ) ) {
return false;
}
if ( ! $this->get_expiry_year() ) {
if ( ! $this->get_expiry_year( 'edit' ) ) {
return false;
}
if ( ! $this->get_expiry_month() ) {
if ( ! $this->get_expiry_month( 'edit' ) ) {
return false;
}
if ( ! $this->get_card_type() ) {
if ( ! $this->get_card_type( 'edit' ) ) {
return false;
}
if ( 4 !== strlen( $this->get_expiry_year() ) ) {
if ( 4 !== strlen( $this->get_expiry_year( 'edit' ) ) ) {
return false;
}
if ( 2 !== strlen( $this->get_expiry_month() ) ) {
if ( 2 !== strlen( $this->get_expiry_month( 'edit' ) ) ) {
return false;
}
@ -65,27 +75,32 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Get type to display to user.
*
* @since 2.6.0
* @param string $context
* @return string
*/
public function get_display_name() {
public function get_display_name( $context = 'view' ) {
/* translators: 1: credit card type 2: last 4 digits 3: expiry month 4: expiry year */
$display = sprintf(
__( '%1$s ending in %2$s (expires %3$s/%4$s)', 'woocommerce' ),
wc_get_credit_card_type_label( $this->get_card_type() ),
$this->get_last4(),
$this->get_expiry_month(),
substr( $this->get_expiry_year(), 2 )
wc_get_credit_card_type_label( $this->get_card_type( $context ) ),
$this->get_last4( $context ),
$this->get_expiry_month( $context ),
substr( $this->get_expiry_year( $context ), 2 )
);
return $display;
}
/**
* Returns the card type (mastercard, visa, ...).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Card type
*/
public function get_card_type() {
return $this->get_meta( 'card_type' );
public function get_card_type( $context = 'view' ) {
return $this->get_meta( 'card_type', true, $context );
}
/**
@ -99,11 +114,13 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Returns the card expiration year (YYYY).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Expiration year
*/
public function get_expiry_year() {
return $this->get_meta( 'expiry_year' );
public function get_expiry_year( $context = 'view' ) {
return $this->get_meta( 'expiry_year', true, $context );
}
/**
@ -117,11 +134,13 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Returns the card expiration month (MM).
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Expiration month
*/
public function get_expiry_month() {
return $this->get_meta( 'expiry_month' );
public function get_expiry_month( $context = 'view' ) {
return $this->get_meta( 'expiry_month', true, $context );
}
/**
@ -135,11 +154,13 @@ class WC_Payment_Token_CC extends WC_Payment_Token {
/**
* Returns the last four digits.
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Last 4 digits
*/
public function get_last4() {
return $this->get_meta( 'last4' );
public function get_last4( $context = 'view' ) {
return $this->get_meta( 'last4', true, $context );
}
/**

View File

@ -10,6 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* Representation of a payment token for eChecks.
*
* @class WC_Payment_Token_eCheck
* @version 2.7.0
* @since 2.6.0
* @category PaymentTokens
* @package WooCommerce/PaymentTokens
@ -20,6 +21,15 @@ class WC_Payment_Token_eCheck extends WC_Payment_Token {
/** @protected string Token Type String */
protected $type = 'eCheck';
/**
* Hook prefix
*
* @since 2.7.0
*/
protected function get_hook_prefix() {
return 'woocommerce_payment_token_echeck_get_';
}
/**
* Validate eCheck payment tokens.
*
@ -34,7 +44,7 @@ class WC_Payment_Token_eCheck extends WC_Payment_Token {
return false;
}
if ( ! $this->get_last4() ) {
if ( ! $this->get_last4( 'edit' ) ) {
return false;
}
return true;
@ -42,19 +52,24 @@ class WC_Payment_Token_eCheck extends WC_Payment_Token {
/**
* Get type to display to user.
*
* @since 2.6.0
* @param string $context
* @return string
*/
public function get_display_name() {
public function get_display_name( $context = 'view' ) {
return __( 'eCheck', 'woocommerce' );
}
/**
* Returns the last four digits.
* @since 2.6.0
*
* @since 2.6.0
* @param string $context
* @return string Last 4 digits
*/
public function get_last4() {
return $this->get_meta( 'last4' );
public function get_last4( $context = 'view' ) {
return $this->get_meta( 'last4', true, $context );
}
/**

View File

@ -92,8 +92,7 @@ class WC_Tests_Payment_Token_CC extends WC_Unit_Test_Case {
public function test_wc_payment_token_cc_read_pulls_meta() {
$token = WC_Helper_Payment_Token::create_cc_token();
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_CC();
$token_read->read( $token_id );
$token_read = new WC_Payment_Token_CC( $token_id );
$this->assertEquals( '1234', $token_read->get_last4() );
}
}

View File

@ -36,9 +36,7 @@ class WC_Tests_Payment_Token_eCheck extends WC_Unit_Test_Case {
$token = WC_Helper_Payment_Token::create_eCheck_token();
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_eCheck();
$token_read->read( $token_id );
$token_read = new WC_Payment_Token_eCheck( $token_id );
$this->assertEquals( '1234', $token_read->get_last4() );
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Class Payment_Token
* @package WooCommerce\Tests\Payment_Tokens
@ -60,7 +59,7 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
* @since 2.6.0
*/
public function test_wc_payment_token_is_default() {
$token = new WC_Payment_Token_Stub( 1 );
$token = new WC_Payment_Token_Stub();
$token->set_default( true );
$this->assertTrue( $token->is_default() );
$token->set_default( false );
@ -79,11 +78,11 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token->set_gateway_id( 'paypal' );
$token->set_extra( 'woocommerce' );
$data = $token->get_data();
$this->assertEquals( $raw_token, $token->get_token() );
$this->assertEquals( 'paypal', $token->get_gateway_id() );
$this->assertEquals( 'stub', $token->get_type() );
$this->assertEquals( $raw_token, $data['token'] );
$this->assertEquals( 'paypal', $data['gateway_id'] );
$this->assertEquals( 'stub', $data['type'] );
$data = $token->get_data();
$this->assertEquals( 'extra', $data['meta_data'][0]->key );
$this->assertEquals( 'woocommerce', $data['meta_data'][0]->value );
}
@ -109,8 +108,7 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_Stub();
$token_read->read( $token_id );
$token_read = new WC_Payment_Token_Stub( $token_id );
$this->assertEquals( $token->get_token(), $token_read->get_token() );
$this->assertEquals( $token->get_extra(), $token_read->get_extra() );
@ -124,7 +122,9 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
$this->assertEquals( __FUNCTION__, $token->get_extra() );
$token->set_extra( ':)' );
$token->update();
$token->save();
$token = new WC_Payment_Token_Stub( $token->get_id() );
$this->assertEquals( ':)', $token->get_extra() );
}
@ -136,9 +136,10 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = new WC_Payment_Token_Stub();
$token->set_extra( __FUNCTION__ );
$token->set_token( time() );
$token->create();
$token->save();
$this->assertNotEmpty( $token->get_id() );
$token = new WC_Payment_Token_Stub( $token->get_id() );
$this->assertEquals( __FUNCTION__, $token->get_extra() );
}
@ -162,4 +163,33 @@ class WC_Tests_Payment_Token extends WC_Unit_Test_Case {
$token = new WC_Payment_Token_Stub();
$this->assertFalse( is_callable( $token, 'get_last4' ) );
}
/**
* Test legacy token functions.
*
* @expectedDeprecated WC_Payment_Token::read
* @expectedDeprecated WC_Payment_Token::update
* @expectedDeprecated WC_Payment_Token::create
* @since 2.7.0
*/
public function test_wc_payment_token_legacy() {
$token = WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
$token_id = $token->get_id();
$token_read = new WC_Payment_Token_Stub();
$token_read->read( $token_id );
$this->assertEquals( $token_id, $token_read->get_id() );
$token = new WC_Payment_Token_Stub();
$token->set_token( 'blah' );
$token->create();
$this->assertEquals( 'blah', $token->get_token() );
$this->assertNotEmpty( $token->get_id() );
$token->set_token( 'blah2' );
$token->update();
$this->assertEquals( 'blah2', $token->get_token() );
}
}

View File

@ -166,14 +166,14 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
$this->assertFalse( $token2->is_default() );
WC_Payment_Tokens::set_users_default( $this->user_id, $token_id_2 );
$token->read( $token_id );
$token2->read( $token_id_2 );
$token = new WC_Payment_Token_CC( $token_id );
$token2 = new WC_Payment_Token_CC( $token_id_2 );
$this->assertFalse( $token->is_default() );
$this->assertTrue( $token2->is_default() );
WC_Payment_Tokens::set_users_default( $this->user_id, $token_id );
$token->read( $token_id );
$token2->read( $token_id_2 );
$token = new WC_Payment_Token_CC( $token_id );
$token2 = new WC_Payment_Token_CC( $token_id_2 );
$this->assertTrue( $token->is_default() );
$this->assertFalse( $token2->is_default() );
}

View File

@ -287,8 +287,10 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/class-wc-data-store.php' ); // WC_Data_Store for CRUD
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-object-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-coupon-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-payment-token-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-coupon-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-payment-token-data-store-table.php' );
$this->query = new WC_Query();
$this->api = new WC_API();