diff --git a/assets/js/frontend/tokenization-form.js b/assets/js/frontend/tokenization-form.js
new file mode 100644
index 00000000000..31eacd3c62e
--- /dev/null
+++ b/assets/js/frontend/tokenization-form.js
@@ -0,0 +1,77 @@
+/*global jQuery, woocommerceTokenizationParams */
+jQuery( function( $ ) {
+
+ var wcTokenizationForm = {
+ gatewayID: woocommerceTokenizationParams.gatewayID,
+ userLoggedIn: woocommerceTokenizationParams.userLoggedIn,
+
+ hideForm: function() {
+ $( '#wc-' + this.gatewayID + '-cc-form, #wc-' + this.gatewayID + '-echeck-form' ).hide();
+ },
+
+ showForm: function() {
+ $( '#wc-' + this.gatewayID + '-cc-form, #wc-' + this.gatewayID + '-echeck-form' ).show();
+ },
+
+ showSaveNewCheckbox: function() {
+ $( '#wc-' + this.gatewayID + '-new-payment-method-wrap' ).show();
+ },
+
+ hideSaveNewCheckbox: function() {
+ $( '#wc-' + this.gatewayID + '-new-payment-method-wrap' ).hide();
+ },
+
+ showSaveNewCheckboxForLoggedInOnly: function() {
+ if ( this.userLoggedIn ) {
+ $( '#wc-' + this.gatewayID + '-new-payment-method-wrap' ).show();
+ } else {
+ $( '#wc-' + this.gatewayID + '-new-payment-method-wrap' ).hide();
+ }
+ }
+ };
+
+ $( document.body ).on( 'updated_checkout', function() {
+
+ // Make sure a radio button (1st) is selected if there is no is_default for this payment method..
+ if ( ! $( 'input[name="wc-' + woocommerceTokenizationParams.gatewayID + '-payment-token"]' ).is( ':checked' ) ) {
+ $( 'input:radio[name="wc-' + woocommerceTokenizationParams.gatewayID + '-payment-token"]:first' ).attr( 'checked', true );
+ if ( 'new' === $( 'input:radio[name="wc-' + woocommerceTokenizationParams.gatewayID + '-payment-token"]:first' ).val() ) {
+ wcTokenizationForm.showForm();
+ wcTokenizationForm.showSaveNewCheckboxForLoggedInOnly();
+ } else {
+ wcTokenizationForm.hideForm();
+ wcTokenizationForm.hideSaveNewCheckbox();
+ }
+ } else {
+ wcTokenizationForm.hideForm();
+ wcTokenizationForm.hideSaveNewCheckbox();
+ }
+
+ // When a radio button is changed, make sure to show/hide our new CC info area
+ $( 'input[name="wc-' + woocommerceTokenizationParams.gatewayID + '-payment-token"]' ).change( function () {
+ if ( 'new' === $( 'input[name="wc-' + woocommerceTokenizationParams.gatewayID + '-payment-token"]:checked' ).val() ) {
+ wcTokenizationForm.showForm();
+ wcTokenizationForm.showSaveNewCheckboxForLoggedInOnly();
+ } else {
+ wcTokenizationForm.hideForm();
+ wcTokenizationForm.hideSaveNewCheckbox();
+ }
+ } );
+
+ // OR if create account is checked
+ $ ( 'input#createaccount' ).change( function() {
+ if ( $( this ).is( ':checked' ) ) {
+ wcTokenizationForm.showSaveNewCheckbox();
+ } else {
+ wcTokenizationForm.hideSaveNewCheckbox();
+ }
+ } );
+
+ // Don't show the "use new" radio button if we are a guest or only have one method..
+ if ( 0 === $( '#wc-' + woocommerceTokenizationParams.gatewayID + '-method-count' ).data( 'count' ) || ! woocommerceTokenizationParams.userLoggedIn ) {
+ $( '.wc-' + woocommerceTokenizationParams.gatewayID + '-payment-form-new-checkbox-wrap' ).hide();
+ }
+
+ } );
+
+} );
diff --git a/assets/js/frontend/tokenization-form.min.js b/assets/js/frontend/tokenization-form.min.js
new file mode 100644
index 00000000000..7af32c3bf8e
--- /dev/null
+++ b/assets/js/frontend/tokenization-form.min.js
@@ -0,0 +1 @@
+jQuery(function(a){var b={gatewayID:woocommerceTokenizationParams.gatewayID,userLoggedIn:woocommerceTokenizationParams.userLoggedIn,hideForm:function(){a("#wc-"+this.gatewayID+"-cc-form, #wc-"+this.gatewayID+"-echeck-form").hide()},showForm:function(){a("#wc-"+this.gatewayID+"-cc-form, #wc-"+this.gatewayID+"-echeck-form").show()},showSaveNewCheckbox:function(){a("#wc-"+this.gatewayID+"-new-payment-method-wrap").show()},hideSaveNewCheckbox:function(){a("#wc-"+this.gatewayID+"-new-payment-method-wrap").hide()},showSaveNewCheckboxForLoggedInOnly:function(){this.userLoggedIn?a("#wc-"+this.gatewayID+"-new-payment-method-wrap").show():a("#wc-"+this.gatewayID+"-new-payment-method-wrap").hide()}};a(document.body).on("updated_checkout",function(){a('input[name="wc-'+woocommerceTokenizationParams.gatewayID+'-payment-token"]').is(":checked")?(b.hideForm(),b.hideSaveNewCheckbox()):(a('input:radio[name="wc-'+woocommerceTokenizationParams.gatewayID+'-payment-token"]:first').attr("checked",!0),"new"===a('input:radio[name="wc-'+woocommerceTokenizationParams.gatewayID+'-payment-token"]:first').val()?(b.showForm(),b.showSaveNewCheckboxForLoggedInOnly()):(b.hideForm(),b.hideSaveNewCheckbox())),a('input[name="wc-'+woocommerceTokenizationParams.gatewayID+'-payment-token"]').change(function(){"new"===a('input[name="wc-'+woocommerceTokenizationParams.gatewayID+'-payment-token"]:checked').val()?(b.showForm(),b.showSaveNewCheckboxForLoggedInOnly()):(b.hideForm(),b.hideSaveNewCheckbox())}),a("input#createaccount").change(function(){a(this).is(":checked")?b.showSaveNewCheckbox():b.hideSaveNewCheckbox()}),0!==a("#wc-"+woocommerceTokenizationParams.gatewayID+"-method-count").data("count")&&woocommerceTokenizationParams.userLoggedIn||a(".wc-"+woocommerceTokenizationParams.gatewayID+"-payment-form-new-checkbox-wrap").hide()})});
\ No newline at end of file
diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php
index 4db15cb9dde..85b9225492d 100644
--- a/includes/abstracts/abstract-wc-order.php
+++ b/includes/abstracts/abstract-wc-order.php
@@ -144,6 +144,39 @@ abstract class WC_Abstract_Order {
}
}
+ /**
+ * Returns a list of all payment tokens associated with the current order
+ *
+ * @since 2.6
+ * @return array An array of payment token objects
+ */
+ public function get_payment_tokens() {
+ return WC_Payment_Tokens::get_order_tokens( $this->id );
+ }
+
+ /**
+ * Add a payment token to an order
+ *
+ * @since 2.6
+ * @param WC_Payment_Token $token Payment token object
+ * @return boolean True if the token was added, false if not
+ */
+ public function add_payment_token( $token ) {
+ if ( empty( $token ) || ! ( $token instanceof WC_Payment_Token ) ) {
+ return false;
+ }
+
+ $token_ids = get_post_meta( $this->id, '_payment_tokens', true );
+ if ( empty ( $token_ids ) ) {
+ $token_ids = array();
+ }
+ $token_ids[] = $token->get_id();
+
+ update_post_meta( $this->id, '_payment_tokens', $token_ids );
+ do_action( 'woocommerce_payment_token_added_to_order', $this->id, $token->get_id(), $token, $token_ids );
+ return true;
+ }
+
/**
* Set the payment method for the order.
*
diff --git a/includes/abstracts/abstract-wc-payment-gateway.php b/includes/abstracts/abstract-wc-payment-gateway.php
index 325e65019a2..ca572655a38 100644
--- a/includes/abstracts/abstract-wc-payment-gateway.php
+++ b/includes/abstracts/abstract-wc-payment-gateway.php
@@ -102,6 +102,36 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
*/
public $view_transaction_url = '';
+ /**
+ * Optional label to show for "new payment method" in the payment
+ * method/token selection radio selection.
+ * @var string
+ */
+ public $new_method_label = '';
+
+ /**
+ * Contains a users saved tokens for this gateway.
+ * @var array
+ */
+ protected $tokens = array();
+
+ /**
+ * Returns a users saved tokens for this gateway.
+ * @since 2.6.0
+ * @return array
+ */
+ public function get_tokens() {
+ if ( sizeof( $this->tokens ) > 0 ) {
+ return $this->tokens;
+ }
+
+ if ( is_user_logged_in() && $this->supports( 'tokenization' ) && is_checkout() ) {
+ $this->tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), $this->id );
+ }
+
+ return $this->tokens;
+ }
+
/**
* Return the title for admin screens.
* @return string
@@ -306,13 +336,12 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
* Override this in your gateway if you have some.
*/
public function payment_fields() {
-
if ( $description = $this->get_description() ) {
echo wpautop( wptexturize( $description ) );
}
if ( $this->supports( 'default_credit_card_form' ) ) {
- $this->credit_card_form();
+ $this->credit_card_form(); // Deprecated, will be removed in a future version.
}
}
@@ -331,48 +360,136 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
}
/**
- * Core credit card form which gateways can used if needed.
- *
+ * Enqueues our tokenization script to handle some of the new form options.
+ * @since 2.6.0
+ */
+ public function tokenization_script() {
+ $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
+ wp_enqueue_script(
+ 'woocommerce-tokenization-form',
+ plugins_url( '/assets/js/frontend/tokenization-form' . $suffix . '.js', WC_PLUGIN_FILE ),
+ array( 'jquery' ),
+ WC()->version
+ );
+ wp_localize_script( 'woocommerce-tokenization-form', 'woocommerceTokenizationParams', array(
+ 'gatewayID' => $this->id,
+ 'userLoggedIn' => (bool) is_user_logged_in(),
+ ) );
+ }
+
+ /**
+ * Grab and display our saved payment methods.
+ * @since 2.6.0
+ */
+ public function saved_payment_methods() {
+ $html = '
';
+ foreach ( $this->get_tokens() as $token ) {
+ $html .= $this->saved_payment_method( $token );
+ }
+ $html .= '
';
+ $html .= '
';
+ echo apply_filters( 'wc_payment_gateway_form_saved_payment_methods_html', $html, $this );
+ }
+
+ /**
+ * Outputs a saved payment method from a token.
+ * @since 2.6.0
+ * @param WC_Payment_Token $token Payment Token
+ * @return string Generated payment method HTML
+ */
+ public function saved_payment_method( $token ) {
+ $html = sprintf(
+ ' ',
+ esc_attr( $this->id ),
+ esc_attr( $token->get_id() ),
+ checked( $token->is_default(), true, false )
+ );
+
+ $html .= sprintf( '',
+ esc_attr( $this->id ),
+ esc_attr( $token->get_id() )
+ );
+
+ $html .= $this->saved_payment_method_title( $token );
+ $html .= ' ';
+
+ return apply_filters( 'wc_payment_gateway_form_saved_payment_method_html', $html, $token, $this );
+ }
+
+ /**
+ * Outputs a saved payment method's title based on the passed token.
+ * @since 2.6.0
+ * @param WC_Payment_Token $token Payment Token
+ * @return string Generated payment method title HTML
+ */
+ public function saved_payment_method_title( $token ) {
+ if ( 'CC' == $token->get_type() && is_callable( array( $token, 'get_card_type' ) ) ) {
+ $type = esc_html__( wc_get_credit_card_type_label( $token->get_card_type() ), 'woocommerce' );
+ } else if ( 'eCheck' === $token->get_type() ) {
+ $type = esc_html__( 'eCheck', 'woocommerce' );
+ }
+
+ $type = apply_filters( 'wc_payment_gateway_form_saved_payment_method_title_type_html', $type, $token, $this );
+ $title = $type;
+
+ if ( is_callable( array( $token, 'get_last4' ) ) ) {
+ $title .= ' ' . sprintf( esc_html__( 'ending in %s', 'woocommerce' ), $token->get_last4() );
+ }
+
+ if ( is_callable( array( $token, 'get_expiry_month' ) ) && is_callable( array( $token, 'get_expiry_year' ) ) ) {
+ $title .= ' ' . sprintf( esc_html__( '(expires %s)', 'woocommerce' ), $token->get_expiry_month() . '/' . substr( $token->get_expiry_year(), 2 ) );
+ }
+
+ return apply_filters( 'wc_payment_gateway_form_saved_payment_method_title_html', $title, $token, $this );
+ }
+
+ /**
+ * Outputs a checkbox for saving a new payment method to the database.
+ * @since 2.6.0
+ */
+ public function save_payment_method_checkbox() {
+ $html = sprintf(
+ '',
+ esc_attr( $this->id )
+ );
+ $html .= sprintf(
+ ' ',
+ esc_attr( $this->id )
+ );
+ $html .= sprintf(
+ '%s ',
+ esc_attr( $this->id ),
+ esc_html__( 'Save to Account', 'woocommerce' )
+ );
+ $html .= '
';
+ echo $html;
+ }
+
+ /**
+ * Displays a radio button for entering a new payment method (new CC details) instead of using a saved method.
+ * Only displayed when a gateway supports tokenization.
+ * @since 2.6.0
+ */
+ public function use_new_payment_method_checkbox() {
+ $label = ( ! empty( $this->new_method_label ) ? esc_html( $this->new_method_label ) : esc_html__( 'Use a new payment method', 'woocommerce' ) );
+ $html = ' ';
+ $html .= '';
+ $html .= apply_filters( 'woocommerce_payment_gateway_form_new_method_label', $label, $this );
+ $html .= ' ';
+ echo '' . $html . '
';
+ }
+
+ /**
+ * Core credit card form which gateways can used if needed. Deprecated - inheirt WC_Payment_Gateway_CC instead.
* @param array $args
* @param array $fields
*/
public function credit_card_form( $args = array(), $fields = array() ) {
-
- wp_enqueue_script( 'wc-credit-card-form' );
-
- $default_args = array(
- 'fields_have_names' => true, // Some gateways like stripe don't need names as the form is tokenized.
- );
-
- $args = wp_parse_args( $args, apply_filters( 'woocommerce_credit_card_form_args', $default_args, $this->id ) );
-
- $default_fields = array(
- 'card-number-field' => '
- ' . __( 'Card Number', 'woocommerce' ) . ' *
-
-
',
- 'card-expiry-field' => '
- ' . __( 'Expiry (MM/YY)', 'woocommerce' ) . ' *
-
-
',
- 'card-cvc-field' => '
- ' . __( 'Card Code', 'woocommerce' ) . ' *
-
-
'
- );
-
- $fields = wp_parse_args( $fields, apply_filters( 'woocommerce_credit_card_form_fields', $default_fields, $this->id ) );
- ?>
-
- id ); ?>
-
- id ); ?>
-
-
- form' );
+ $cc_form = new WC_Payment_Gateway_CC;
+ $cc_form->id = $this->id;
+ $cc_form->supports = $this->supports;
+ $cc_form->form();
}
+
}
diff --git a/includes/abstracts/abstract-wc-payment-token.php b/includes/abstracts/abstract-wc-payment-token.php
new file mode 100644
index 00000000000..226555b88a7
--- /dev/null
+++ b/includes/abstracts/abstract-wc-payment-token.php
@@ -0,0 +1,264 @@
+id = $id;
+ $this->data = $data;
+ $this->data['type'] = $this->type;
+ $this->meta = $meta;
+ }
+
+ /**
+ * Returns the payment token ID.
+ * @since 2.6.0
+ * @return ID Token ID
+ */
+ public function get_id() {
+ return absint( $this->id );
+ }
+
+ /**
+ * Returns the raw payment token.
+ * @since 2.6.0
+ * @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;
+ }
+
+ /**
+ * Returns the type of this payment token (CC, eCheck, or something else).
+ * @since 2.6.0
+ * @return string Payment Token Type (CC, eCheck)
+ */
+ public function get_type() {
+ return isset( $this->data['type'] ) ? $this->data['type'] : '';
+ }
+
+ /**
+ * Returns the user ID associated with the token or false if this token is not associated.
+ * @since 2.6.0
+ * @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'] = $user_id;
+ }
+
+ /**
+ * Returns the ID of the gateway associated with this payment token.
+ * @since 2.6.0
+ * @return string Gateway ID
+ */
+ public function get_gateway_id() {
+ return $this->data['gateway_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'] );
+ }
+
+ /**
+ * Marks the payment as default or non-default.
+ * @since 2.6.0
+ * @param boolean $is_default True or false
+ */
+ public function set_default( $is_default ) {
+ $this->data['is_default'] = (bool) $is_default;
+ }
+
+ /**
+ * Returns a dump of the token data (combined data and meta).
+ * @since 2.6.0
+ * @return mixed array representation
+ */
+ public function get_data() {
+ return array_merge( $this->data, array( 'meta' => $this->meta ) );
+ }
+
+ /**
+ * Validate basic token info (token and type are required).
+ * @since 2.6.0
+ * @return boolean True if the passed data is valid
+ */
+ public function validate() {
+ if ( empty( $this->data['token'] ) ) {
+ return false;
+ }
+
+ if ( empty( $this->data['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 ) ) ) {
+ $this->id = $token->token_id;
+ $token = (array) $token;
+ unset( $token['token_id'] );
+ $this->data = $token;
+ $meta = get_metadata( 'payment_token', $token_id );
+ $passed_meta = array();
+ if ( ! empty( $meta ) ) {
+ foreach( $meta as $meta_key => $meta_value ) {
+ $passed_meta[ $meta_key ] = $meta_value[0];
+ }
+ }
+ $this->meta = $passed_meta;
+ }
+ }
+
+ /**
+ * Update a payment token.
+ * @since 2.6.0
+ * @return True 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;
+
+ $wpdb->update( $wpdb->prefix . 'woocommerce_payment_tokens', $this->data, array( 'token_id' => $this->get_id() ) );
+ foreach ( $this->meta as $meta_key => $meta_value ) {
+ update_metadata( 'payment_token', $this->get_id(), $meta_key, $meta_value );
+ }
+
+ do_action( 'woocommerce_payment_token_updated', $this->get_id() );
+ return true;
+ }
+
+ /**
+ * Create a new payment token in the database.
+ * @since 2.6.0
+ * @return True 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() && is_user_logged_in() ) {
+ $default_token = WC_Payment_Tokens::get_customer_default_token( get_current_user_id() );
+ if ( is_null( $default_token ) ) {
+ $this->set_default( true );
+ }
+ }
+
+ $wpdb->insert( $wpdb->prefix . 'woocommerce_payment_tokens', $this->data );
+ $this->id = $token_id = $wpdb->insert_id;
+ foreach ( $this->meta as $meta_key => $meta_value ) {
+ add_metadata( 'payment_token', $token_id, $meta_key, $meta_value, true );
+ }
+
+ 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 True 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() {
+ 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 );
+ }
+
+}
diff --git a/includes/admin/settings/class-wc-settings-checkout.php b/includes/admin/settings/class-wc-settings-checkout.php
index ed8f5306532..02b902bb68d 100644
--- a/includes/admin/settings/class-wc-settings-checkout.php
+++ b/includes/admin/settings/class-wc-settings-checkout.php
@@ -201,6 +201,25 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
'desc_tip' => true,
),
+ array(
+ 'title' => __( 'Delete Payment Method', 'woocommerce' ),
+ 'desc' => __( 'Endpoint for the delete payment method page', 'woocommerce' ),
+ 'id' => 'woocommerce_myaccount_delete_payment_method_endpoint',
+ 'type' => 'text',
+ 'default' => 'delete-payment-method',
+ 'desc_tip' => true,
+ ),
+
+ array(
+ 'title' => __( 'Set Default Payment Method', 'woocommerce' ),
+ 'desc' => __( 'Endpoint for the setting a default payment page', 'woocommerce' ),
+ 'id' => 'woocommerce_myaccount_set_default_payment_method_endpoint',
+ 'type' => 'text',
+ 'default' => 'set-default-payment-method',
+ 'desc_tip' => true,
+ ),
+
+
array(
'type' => 'sectionend',
'id' => 'checkout_endpoint_options',
diff --git a/includes/admin/views/html-admin-page-status-report.php b/includes/admin/views/html-admin-page-status-report.php
index 72de1214608..f278552bcca 100644
--- a/includes/admin/views/html-admin-page-status-report.php
+++ b/includes/admin/views/html-admin-page-status-report.php
@@ -334,6 +334,8 @@ if ( ! defined( 'ABSPATH' ) ) {
'woocommerce_shipping_zones',
'woocommerce_shipping_zone_locations',
'woocommerce_shipping_zone_methods',
+ 'woocommerce_payment_tokens',
+ 'woocommerce_payment_tokenmeta',
);
foreach ( $tables as $table ) {
diff --git a/includes/class-wc-autoloader.php b/includes/class-wc-autoloader.php
index f6af30d3d6a..a891fdf3e43 100644
--- a/includes/class-wc-autoloader.php
+++ b/includes/class-wc-autoloader.php
@@ -83,6 +83,8 @@ class WC_Autoloader {
$path = $this->include_path . 'admin/';
} elseif ( strpos( $class, 'wc_cli_' ) === 0 ) {
$path = $this->include_path . 'cli/';
+ } elseif ( strpos( $class, 'wc_payment_token_' ) === 0 ) {
+ $path = $this->include_path . 'payment-tokens/';
}
if ( empty( $path ) || ( ! $this->load_file( $path . $file ) && strpos( $class, 'wc_' ) === 0 ) ) {
diff --git a/includes/class-wc-form-handler.php b/includes/class-wc-form-handler.php
index ba42eff65e6..a59ed5c1975 100644
--- a/includes/class-wc-form-handler.php
+++ b/includes/class-wc-form-handler.php
@@ -359,17 +359,15 @@ class WC_Form_Handler {
$payment_method = wc_clean( $_POST['payment_method'] );
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
-
// Validate
$available_gateways[ $payment_method ]->validate_fields();
// Process
if ( wc_notice_count( 'wc_errors' ) == 0 ) {
$result = $available_gateways[ $payment_method ]->add_payment_method();
-
// Redirect to success/confirmation/payment page
if ( $result['result'] == 'success' ) {
- wc_add_message( __( 'Payment method added.', 'woocommerce' ) );
+ wc_add_notice( __( 'Payment method added.', 'woocommerce' ) );
wp_redirect( $result['redirect'] );
exit();
}
diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php
index 25f6addaedb..c2f9e5d7c7e 100644
--- a/includes/class-wc-install.php
+++ b/includes/class-wc-install.php
@@ -484,6 +484,25 @@ CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods (
method_id varchar(255) NOT NULL,
method_order bigint(20) NOT NULL,
PRIMARY KEY (instance_id)
+) $collate;
+CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokens (
+ token_id bigint(20) NOT NULL auto_increment,
+ gateway_id varchar(255) NOT NULL,
+ token text NOT NULL,
+ user_id bigint(20) NOT NULL DEFAULT '0',
+ type varchar(255) NOT NULL,
+ is_default tinyint(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (token_id),
+ KEY user_id (user_id)
+) $collate;
+CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokenmeta (
+ meta_id bigint(20) NOT NULL auto_increment,
+ payment_token_id bigint(20) NOT NULL,
+ meta_key varchar(255) NULL,
+ meta_value longtext NULL,
+ PRIMARY KEY (meta_id),
+ KEY payment_token_id (payment_token_id),
+ KEY meta_key (meta_key)
) $collate;
";
diff --git a/includes/class-wc-payment-gateways.php b/includes/class-wc-payment-gateways.php
index a7c613d9708..eb20438d925 100644
--- a/includes/class-wc-payment-gateways.php
+++ b/includes/class-wc-payment-gateways.php
@@ -146,7 +146,9 @@ class WC_Payment_Gateways {
if ( $gateway->is_available() ) {
if ( ! is_add_payment_method_page() ) {
$_available_gateways[ $gateway->id ] = $gateway;
- } elseif( $gateway->supports( 'add_payment_method' ) ) {
+ } else if( $gateway->supports( 'add_payment_method' ) ) {
+ $_available_gateways[ $gateway->id ] = $gateway;
+ } else if ( $gateway->supports( 'tokenization' ) ) {
$_available_gateways[ $gateway->id ] = $gateway;
}
}
@@ -166,7 +168,14 @@ class WC_Payment_Gateways {
return;
}
- $current = WC()->session->get( 'chosen_payment_method' );
+ if ( is_user_logged_in() ) {
+ $default_token = WC_Payment_Tokens::get_customer_default_token( get_current_user_id() );
+ if ( ! is_null( $default_token ) ) {
+ $default_token_gateway = $default_token->get_gateway_id();
+ }
+ }
+
+ $current = ( isset( $default_token_gateway ) ? $default_token_gateway : WC()->session->get( 'chosen_payment_method' ) );
if ( $current && isset( $gateways[ $current ] ) ) {
$current_gateway = $gateways[ $current ];
diff --git a/includes/class-wc-payment-tokens.php b/includes/class-wc-payment-tokens.php
new file mode 100644
index 00000000000..0f54f6c6f47
--- /dev/null
+++ b/includes/class-wc-payment-tokens.php
@@ -0,0 +1,198 @@
+get_results( $wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE user_id = %d",
+ $customer_id
+ ) );
+
+ if ( empty( $token_results ) ) {
+ return array();
+ }
+
+ $tokens = array();
+ foreach ( $token_results as $token_result ) {
+ if ( empty( $gateway_id ) || $gateway_id === $token_result->gateway_id ) {
+ $_token = self::get( $token_result->token_id, $token_result );
+ if ( ! empty( $_token ) ) {
+ $tokens[ $token_result->token_id ] = $_token;
+ }
+ }
+ }
+
+ return apply_filters( 'woocommerce_get_customer_payment_tokens', $tokens, $customer_id );
+ }
+
+ /**
+ * 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
+ */
+ public static function get_customer_default_token( $customer_id ) {
+ if ( $customer_id < 1 ) {
+ 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
+ ) );
+
+ if ( $token ) {
+ return self::get( $token->token_id, $token );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 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
+ */
+ public static function get_order_tokens( $order_id ) {
+ $order = wc_get_order( $order_id );
+
+ if ( ! $order ) {
+ return array();
+ }
+
+ $token_ids = get_post_meta( $order_id, '_payment_tokens', true );
+ if ( empty ( $token_ids ) ) {
+ return array();
+ }
+
+ global $wpdb;
+
+ $token_ids_as_string = implode( ',', array_map( 'intval', $token_ids ) );
+ $token_results = $wpdb->get_results(
+ "SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token_id IN ( {$token_ids_as_string} )"
+ );
+
+ if ( empty( $token_results ) ) {
+ return array();
+ }
+
+ $tokens = array();
+ foreach ( $token_results as $token_result ) {
+ $_token = self::get( $token_result->token_id, $token_result );
+ if ( ! empty( $_token ) ) {
+ $tokens[ $token_result->token_id ] = $_token;
+ }
+ }
+
+ return apply_filters( 'woocommerce_get_order_payment_tokens', $tokens, $order_id );
+ }
+
+ /**
+ * 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;
+ 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
+ ) );
+ // Still empty? Token doesn't exist? Don't continue
+ if ( empty( $token_result ) ) {
+ return null;
+ }
+ }
+ $token_class = 'WC_Payment_Token_' . $token_result->type;
+ if ( class_exists( $token_class ) ) {
+ $meta = get_metadata( 'payment_token', $token_id );
+ $passed_meta = array();
+ if ( ! empty( $meta ) ) {
+ foreach( $meta as $meta_key => $meta_value ) {
+ $passed_meta[ $meta_key ] = $meta_value[0];
+ }
+ }
+ return new $token_class( $token_id, (array) $token_result, $passed_meta );
+ }
+ }
+
+ /**
+ * Remove a payment token from the database by ID.
+ * @since 2.6.0
+ * @param WC_Payment_Token $token_id Token ID
+ */
+ public static function delete( $token_id ) {
+ $type = self::get_token_type_by_id( $token_id );
+ if ( ! empty ( $type ) ) {
+ $class = 'WC_Payment_Token_' . $type;
+ $token = new $class( $token_id );
+ $token->delete();
+ }
+ }
+
+ /**
+ * 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 ) {
+ $users_tokens = self::get_customer_tokens( $user_id );
+ foreach ( $users_tokens as $token ) {
+ if ( $token_id === $token->get_id() ) {
+ $token->set_default( true );
+ } else {
+ $token->set_default( false );
+ }
+ $token->update();
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+}
diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php
index 1645af43adf..ebb36787b16 100644
--- a/includes/class-wc-query.php
+++ b/includes/class-wc-query.php
@@ -65,17 +65,18 @@ class WC_Query {
// Checkout actions.
'order-pay' => get_option( 'woocommerce_checkout_pay_endpoint', 'order-pay' ),
'order-received' => get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ),
-
// My account actions.
- 'orders' => get_option( 'woocommerce_myaccount_orders_endpoint', 'orders' ),
- 'view-order' => get_option( 'woocommerce_myaccount_view_order_endpoint', 'view-order' ),
- 'downloads' => get_option( 'woocommerce_myaccount_downloads_endpoint', 'downloads' ),
- 'edit-account' => get_option( 'woocommerce_myaccount_edit_account_endpoint', 'edit-account' ),
- 'edit-address' => get_option( 'woocommerce_myaccount_edit_address_endpoint', 'edit-address' ),
- 'payment-methods' => get_option( 'woocommerce_myaccount_payment_methods_endpoint', 'payment-methods' ),
- 'lost-password' => get_option( 'woocommerce_myaccount_lost_password_endpoint', 'lost-password' ),
- 'customer-logout' => get_option( 'woocommerce_logout_endpoint', 'customer-logout' ),
- 'add-payment-method' => get_option( 'woocommerce_myaccount_add_payment_method_endpoint', 'add-payment-method' ),
+ 'orders' => get_option( 'woocommerce_myaccount_orders_endpoint', 'orders' ),
+ 'view-order' => get_option( 'woocommerce_myaccount_view_order_endpoint', 'view-order' ),
+ 'downloads' => get_option( 'woocommerce_myaccount_downloads_endpoint', 'downloads' ),
+ 'edit-account' => get_option( 'woocommerce_myaccount_edit_account_endpoint', 'edit-account' ),
+ 'edit-address' => get_option( 'woocommerce_myaccount_edit_address_endpoint', 'edit-address' ),
+ 'payment-methods' => get_option( 'woocommerce_myaccount_payment_methods_endpoint', 'payment-methods' ),
+ 'lost-password' => get_option( 'woocommerce_myaccount_lost_password_endpoint', 'lost-password' ),
+ 'customer-logout' => get_option( 'woocommerce_logout_endpoint', 'customer-logout' ),
+ 'add-payment-method' => get_option( 'woocommerce_myaccount_add_payment_method_endpoint', 'add-payment-method' ),
+ 'delete-payment-method' => get_option( 'woocommerce_myaccount_delete_payment_method_endpoint', 'delete-payment-method' ),
+ 'set-default-payment-method' => get_option( 'woocommerce_myaccount_set_default_payment_method_endpoint', 'set-default-payment-method' ),
);
}
diff --git a/includes/gateways/class-wc-payment-gateway-cc.php b/includes/gateways/class-wc-payment-gateway-cc.php
new file mode 100644
index 00000000000..e560efed26e
--- /dev/null
+++ b/includes/gateways/class-wc-payment-gateway-cc.php
@@ -0,0 +1,85 @@
+supports( 'tokenization' ) && is_checkout();
+
+ if ( $display_tokenization ) {
+ $this->tokenization_script();
+ if ( is_user_logged_in() ) {
+ $this->saved_payment_methods();
+ }
+ $this->use_new_payment_method_checkbox();
+ }
+
+ $this->form();
+
+ if ( $display_tokenization ) {
+ $this->save_payment_method_checkbox();
+ }
+ }
+
+ /**
+ * Outputs fields for entering credit card information.
+ * @since 2.6.0
+ */
+ public function form() {
+ $html = '';
+ $fields = array();
+
+ $cvc_field = '
+ ' . __( 'Card Code', 'woocommerce' ) . ' *
+
+
';
+
+ $default_fields = array(
+ 'card-number-field' => '
+ ' . __( 'Card Number', 'woocommerce' ) . ' *
+
+
',
+ 'card-expiry-field' => '
+ ' . __( 'Expiry (MM/YY)', 'woocommerce' ) . ' *
+
+
'
+ );
+
+ if ( ! $this->supports( 'credit_card_form_cvc_on_saved_method' ) ) {
+ $default_fields['card-cvc-field'] = $cvc_field;
+ }
+
+ $fields = wp_parse_args( $fields, apply_filters( 'woocommerce_credit_card_form_fields', $default_fields, $this->id ) );
+ ?>
+
+
+ id ); ?>
+
+ id ); ?>
+
+
+ supports( 'credit_card_form_cvc_on_saved_method' ) ) {
+ echo '' . $cvc_field . ' ';
+ }
+ }
+
+}
diff --git a/includes/gateways/class-wc-payment-gateway-echeck.php b/includes/gateways/class-wc-payment-gateway-echeck.php
new file mode 100644
index 00000000000..a5ab6b446e8
--- /dev/null
+++ b/includes/gateways/class-wc-payment-gateway-echeck.php
@@ -0,0 +1,72 @@
+supports( 'tokenization' ) && is_checkout();
+
+ if ( $display_tokenization ) {
+ $this->tokenization_script();
+ if ( is_user_logged_in() ) {
+ $this->saved_payment_methods();
+ }
+ $this->use_new_payment_method_checkbox();
+ }
+
+ $this->form();
+
+ if ( $display_tokenization ) {
+ $this->save_payment_method_checkbox();
+ }
+ }
+
+ /**
+ * Outputs fields for entering eCheck information.
+ * @since 2.6.0
+ */
+ public function form() {
+ $html = '';
+ $fields = array();
+
+ $default_fields = array(
+ 'routing-number' => '
+ ' . __( 'Routing Number', 'woocommerce' ) . ' *
+
+
',
+ 'account-number' => '
+ ' . __( 'Account Number', 'woocommerce' ) . ' *
+
+
',
+ );
+
+ $fields = wp_parse_args( $fields, apply_filters( 'woocommerce_echeck_form_fields', $default_fields, $this->id ) );
+ ?>
+
+
+ id ); ?>
+
+ id ); ?>
+
+ h;h++)g+=""+Simplify_commerce_params[e[h].field]+" "+Simplify_commerce_params.is_invalid+" - "+e[h].message+". ";d.prepend('")}}else d.append(' '),c.submit()}a(function(){a(document.body).on("checkout_error",function(){a(".simplify-token").remove()}),a("form.checkout").on("checkout_place_order_simplify_commerce",function(){return b()}),a("form#order_review").on("submit",function(){return b()}),a("form.checkout, form#order_review").on("change","#simplify_commerce-cc-form input",function(){a(".simplify-token").remove()})})}(jQuery);
\ No newline at end of file
+!function(a){function b(){var b=a("form.checkout, form#order_review, form#add_payment_method");if((a("#payment_method_simplify_commerce").is(":checked")&&a("#wc-simplify_commerce-new").is(":checked")||"1"===a("#woocommerce_add_payment_method").val())&&0===a("input.simplify-token").length){b.block({message:null,overlayCSS:{background:"#fff",opacity:.6}});var d=a("#simplify_commerce-card-number").val(),e=a("#simplify_commerce-card-cvc").val(),f=a.payment.cardExpiryVal(a("#simplify_commerce-card-expiry").val()),g=b.find("#billing_address_1").val(),h=b.find("#billing_address_2").val(),i=b.find("#billing_country").val(),j=b.find("#billing_state").val(),k=b.find("#billing_city").val(),l=b.find("#billing_postcode").val();return d=d.replace(/\s/g,""),SimplifyCommerce.generateToken({key:Simplify_commerce_params.key,card:{number:d,cvc:e,expMonth:f.month,expYear:f.year-2e3,addressLine1:g,addressLine2:h,addressCountry:i,addressState:j,addressZip:l,addressCity:k}},c),!1}return!0}function c(b){var c=a("form.checkout, form#order_review, form#add_payment_method"),d=a("#wc-simplify_commerce-cc-form");if(b.error){if(a(".woocommerce-error, .simplify-token",d).remove(),c.unblock(),"validation"===b.error.code){for(var e=b.error.fieldErrors,f=e.length,g="",h=0;f>h;h++)g+=""+Simplify_commerce_params[e[h].field]+" "+Simplify_commerce_params.is_invalid+" - "+e[h].message+". ";d.prepend('")}}else d.append(' '),c.submit()}a(function(){a(document.body).on("checkout_error",function(){a(".simplify-token").remove()}),a("form.checkout").on("checkout_place_order_simplify_commerce",function(){return b()}),a("form#order_review").on("submit",function(){return b()}),a("form#add_payment_method").on("submit",function(){return b()}),a("form.checkout, form#order_review, form#add_payment_method").on("change","#wc-simplify_commerce-cc-form input",function(){a(".simplify-token").remove()})})}(jQuery);
\ No newline at end of file
diff --git a/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php b/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php
index a3adada79b6..0e56d4cde43 100644
--- a/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php
+++ b/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php
@@ -8,13 +8,13 @@ if ( ! defined( 'ABSPATH' ) ) {
* Simplify Commerce Gateway.
*
* @class WC_Gateway_Simplify_Commerce
- * @extends WC_Payment_Gateway
+ * @extends WC_Payment_Gateway_CC
* @since 2.2.0
* @version 1.0.0
* @package WooCommerce/Classes/Payment
* @author WooThemes
*/
-class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
+class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway_CC {
/**
* Constructor.
@@ -23,6 +23,7 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
$this->id = 'simplify_commerce';
$this->method_title = __( 'Simplify Commerce', 'woocommerce' );
$this->method_description = __( 'Take payments via Simplify Commerce - uses simplify.js to create card tokens and the Simplify Commerce SDK. Requires SSL when sandbox is disabled.', 'woocommerce' );
+ $this->new_method_label = __( 'Use a new card', 'woocommerce' );
$this->has_fields = true;
$this->supports = array(
'subscriptions',
@@ -37,6 +38,7 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
'subscription_date_changes',
'multiple_subscriptions',
'default_credit_card_form',
+ 'tokenization',
'refunds',
'pre-orders'
);
@@ -274,7 +276,7 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
}
if ( 'standard' == $this->mode ) {
- $this->credit_card_form( array( 'fields_have_names' => false ) );
+ parent::payment_fields();
}
}
@@ -282,7 +284,16 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
* Outputs scripts used for simplify payment.
*/
public function payment_scripts() {
- if ( ! is_checkout() || ! $this->is_available() ) {
+ $load_scripts = false;
+
+ if ( is_checkout() ) {
+ $load_scripts = true;
+ }
+ if ( $this->is_available() ) {
+ $load_scripts = true;
+ }
+
+ if ( false === $load_scripts ) {
return;
}
@@ -301,6 +312,98 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
) );
}
+ public function add_payment_method() {
+ if ( empty ( $_POST['simplify_token'] ) ) {
+ wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' );
+ return;
+ }
+
+ $cart_token = wc_clean( $_POST['simplify_token'] );
+ $customer_token = $this->get_users_token();
+ $current_user = wp_get_current_user();
+ $customer_info = array(
+ 'email' => $current_user->user_email,
+ 'name' => $current_user->display_name,
+ );
+
+ $token = $this->save_token( $customer_token, $cart_token, $customer_info );
+ if ( is_null( $token ) ) {
+ wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' );
+ return;
+ }
+
+ return array(
+ 'result' => 'success',
+ 'redirect' => wc_get_endpoint_url( 'payment-methods' ),
+ );
+ }
+
+ /**
+ * Actualy saves a customer token to the database.
+ *
+ * @param WC_Payment_Token $customer_token Payment Token
+ * @param string $cart_token CC Token
+ * @param array $customer_info 'email', 'name'
+ */
+ public function save_token( $customer_token, $cart_token, $customer_info ) {
+ if ( ! is_null( $customer_token ) ) {
+ $customer = Simplify_Customer::findCustomer( $customer_token->get_token() );
+ $updates = array( 'token' => $cart_token );
+ $customer->setAll( $updates );
+ $customer->updateCustomer();
+ $customer = Simplify_Customer::findCustomer( $customer_token->get_token() ); // get updated customer with new set card
+ $token = $customer_token;
+ } else {
+ $customer = Simplify_Customer::createCustomer( array(
+ 'token' => $cart_token,
+ 'email' => $customer_info['email'],
+ 'name' => $customer_info['name'],
+ ) );
+ $token = new WC_Payment_Token_CC();
+ $token->set_token( $customer->id );
+ }
+
+ // If we were able to create an save our card, save the data on our side too
+ if ( is_object( $customer ) && '' != $customer->id ) {
+ $customer_properties = $customer->getProperties();
+ $card = $customer_properties['card'];
+ $token->set_gateway_id( $this->id );
+ $token->set_card_type( strtolower( $card->type ) );
+ $token->set_last4( $card->last4 );
+ $expiry_month = ( 1 === strlen( $card->expMonth ) ? '0' . $card->expMonth : $card->expMonth );
+ $token->set_expiry_month( $expiry_month );
+ $token->set_expiry_year( '20' . $card->expYear );
+ if ( is_user_logged_in() ) {
+ $token->set_user_id( get_current_user_id() );
+ }
+ $token->save();
+ return $token;
+ }
+
+ return null;
+ }
+
+ /**
+ * Process customer: updating or creating a new customer/saved CC
+ *
+ * @param WC_Order $order Order object
+ * @param WC_Payment_Token $customer_token Payment Token
+ * @param string $cart_token CC Token
+ */
+ protected function process_customer( $order, $customer_token = null, $cart_token = '' ) {
+ // Are we saving a new payment method?
+ if ( is_user_logged_in() && isset( $_POST['wc-simplify_commerce-new-payment-method'] ) && true === (bool) $_POST['wc-simplify_commerce-new-payment-method'] ) {
+ $customer_info = array(
+ 'email' => $order->billing_email,
+ 'name' => trim( $order->get_formatted_billing_full_name() ),
+ );
+ $token = $this->save_token( $customer_token, $cart_token, $customer_info );
+ if ( ! is_null( $token ) ) {
+ $order->add_payment_token( $token );
+ }
+ }
+ }
+
/**
* Process standard payments.
*
@@ -310,10 +413,10 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
* @uses Simplify_BadRequestException
* @return array
*/
- protected function process_standard_payments( $order, $cart_token = '' ) {
+ protected function process_standard_payments( $order, $cart_token = '', $customer_token = '' ) {
try {
- if ( empty( $cart_token ) ) {
+ if ( empty( $cart_token ) && empty( $customer_token ) ) {
$error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' );
if ( 'yes' == $this->sandbox ) {
@@ -323,26 +426,44 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
throw new Simplify_ApiException( $error_msg );
}
- $payment = Simplify_Payment::createPayment( array(
- 'amount' => $order->order_total * 100, // In cents.
- 'token' => $cart_token,
- 'description' => sprintf( __( '%s - Order #%s', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order->get_order_number() ),
- 'currency' => strtoupper( get_woocommerce_currency() ),
- 'reference' => $order->id
- ) );
+ // We need to figure out if we want to charge the card token (new unsaved token, no customer, etc)
+ // or the customer token (just saved method, previously saved method)
+ $pass_tokens = array();
- $order_complete = $this->process_order_status( $order, $payment->id, $payment->paymentStatus, $payment->authCode );
+ if ( ! empty ( $cart_token ) ) {
+ $pass_tokens['token'] = $cart_token;
+ }
+
+ if ( ! empty ( $customer_token ) ) {
+ $pass_tokens['customer'] = $customer_token;
+ // Use the customer token only, since we already saved the (one time use) card token to the customer
+ if ( isset( $_POST['wc-simplify_commerce-new-payment-method'] ) && true === (bool) $_POST['wc-simplify_commerce-new-payment-method'] ) {
+ unset( $pass_tokens['token'] );
+ }
+ }
+
+ // Did we create an account and save a payment method? We might need to use the customer token instead of the card token
+ if ( isset( $_POST['createaccount'] ) && true === (bool) $_POST['createaccount'] && empty ( $customer_token ) ) {
+ $user_token = $this->get_users_token();
+ if ( ! is_null( $user_token ) ) {
+ $pass_tokens['customer'] = $user_token->get_token();
+ unset( $pass_tokens['token'] );
+ }
+ }
+
+ $payment_response = $this->do_payment( $order, $order->get_total(), $pass_tokens );
+
+ if ( is_wp_error( $payment_response ) ) {
+ throw new Exception( $payment_response->get_error_message() );
+ } else {
+ // Remove cart
+ WC()->cart->empty_cart();
- if ( $order_complete ) {
// Return thank you page redirect
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
- } else {
- $order->add_order_note( __( 'Simplify payment declined', 'woocommerce' ) );
-
- throw new Simplify_ApiException( __( 'Payment was declined - please try another card.', 'woocommerce' ) );
}
} catch ( Simplify_ApiException $e ) {
@@ -361,6 +482,62 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
}
}
+ /**
+ * do payment function.
+ *
+ * @param WC_order $order
+ * @param int $amount (default: 0)
+ * @uses Simplify_BadRequestException
+ * @return bool|WP_Error
+ */
+ public function do_payment( $order, $amount = 0, $token = array() ) {
+ if ( $amount * 100 < 50 ) {
+ return new WP_Error( 'simplify_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' ) );
+ }
+
+ try {
+ // Charge the customer
+ $data = array(
+ 'amount' => $amount * 100, // In cents.
+ 'description' => sprintf( __( '%s - Order #%s', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order->get_order_number() ),
+ 'currency' => strtoupper( get_woocommerce_currency() ),
+ 'reference' => $order->id
+ );
+
+ $data = array_merge( $data, $token );
+ $payment = Simplify_Payment::createPayment( $data );
+
+ } catch ( Exception $e ) {
+
+ $error_message = $e->getMessage();
+
+ if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
+ $error_message = '';
+ foreach ( $e->getFieldErrors() as $error ) {
+ $error_message .= ' ' . $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')';
+ }
+ }
+
+ $order->add_order_note( sprintf( __( 'Simplify payment error: %s', 'woocommerce' ), $error_message ) );
+
+ return new WP_Error( 'simplify_payment_declined', $e->getMessage(), array( 'status' => $e->getCode() ) );
+ }
+
+ if ( 'APPROVED' == $payment->paymentStatus ) {
+ // Payment complete
+ $order->payment_complete( $payment->id );
+
+ // Add order note
+ $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %s, Auth Code: %s)', 'woocommerce' ), $payment->id, $payment->authCode ) );
+
+ return true;
+ } else {
+ $order->add_order_note( __( 'Simplify payment declined', 'woocommerce' ) );
+
+ return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - please try another card.', 'woocommerce' ) );
+ }
+ }
+
/**
* Process standard payments.
*
@@ -374,19 +551,52 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
);
}
+ protected function get_users_token() {
+ $customer_token = null;
+ if ( is_user_logged_in() ) {
+ $tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id() ) ;
+ foreach ( $tokens as $token ) {
+ if ( $token->get_gateway_id() === $this->id ) {
+ $customer_token = $token;
+ break;
+ }
+ }
+ }
+ return $customer_token;
+ }
+
/**
* Process the payment.
*
* @param int $order_id
*/
public function process_payment( $order_id ) {
- $cart_token = isset( $_POST['simplify_token'] ) ? wc_clean( $_POST['simplify_token'] ) : '';
- $order = wc_get_order( $order_id );
+ $order = wc_get_order( $order_id );
- if ( 'hosted' == $this->mode ) {
+ // Payment/CC form is hosted on Simplify
+ if ( 'hosted' === $this->mode ) {
return $this->process_hosted_payments( $order );
- } else {
- return $this->process_standard_payments( $order, $cart_token );
+ }
+
+ // New CC info was entered
+ if ( isset( $_POST['simplify_token'] ) ) {
+ $cart_token = wc_clean( $_POST['simplify_token'] );
+ $customer_token = $this->get_users_token();
+ $customer_token_value = ( ! is_null( $customer_token ) ? $customer_token->get_token() : '' );
+ $this->process_customer( $order, $customer_token, $cart_token );
+ return $this->process_standard_payments( $order, $cart_token, $customer_token_value );
+ }
+
+ // Possibly Create (or update) customer/save payment token, use an existing token, and then process the payment
+ if ( isset( $_POST['wc-simplify_commerce-payment-token'] ) && 'new' !== $_POST['wc-simplify_commerce-payment-token'] ) {
+ $token_id = wc_clean( $_POST['wc-simplify_commerce-payment-token'] );
+ $token = WC_Payment_Tokens::get( $token_id );
+ if ( $token->get_user_id() !== get_current_user_id() ) {
+ wc_add_notice( __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' ), 'error' );
+ return;
+ }
+ $this->process_customer( $order, $token );
+ return $this->process_standard_payments( $order, '', $token->get_token() );
}
}
@@ -411,7 +621,8 @@ class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway {
'address-city' => $order->billing_city,
'address-state' => $order->billing_state,
'address-zip' => $order->billing_postcode,
- 'address-country' => $order->billing_country
+ 'address-country' => $order->billing_country,
+ 'operation' => 'create.token',
), $order->id );
return $args;
diff --git a/includes/payment-tokens/class-wc-payment-token-cc.php b/includes/payment-tokens/class-wc-payment-token-cc.php
new file mode 100644
index 00000000000..ca874345dc2
--- /dev/null
+++ b/includes/payment-tokens/class-wc-payment-token-cc.php
@@ -0,0 +1,139 @@
+meta['last4'] ) ) {
+ return false;
+ }
+
+ if ( empty( $this->meta['expiry_year'] ) ) {
+ return false;
+ }
+
+ if ( empty( $this->meta['expiry_month'] ) ) {
+ return false;
+ }
+
+ if ( empty ( $this->meta['card_type'] ) ) {
+ return false;
+ }
+
+ if ( 4 !== strlen( $this->meta['expiry_year'] ) ) {
+ return false;
+ }
+
+ if ( 2 !== strlen( $this->meta['expiry_month'] ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the card type (mastercard, visa, ...).
+ * @since 2.6.0
+ * @return string Card type
+ */
+ public function get_card_type() {
+ return isset( $this->meta['card_type'] ) ? $this->meta['card_type'] : null;
+ }
+
+ /**
+ * Set the card type (mastercard, visa, ...).
+ * @since 2.6.0
+ * @param string $type
+ */
+ public function set_card_type( $type ) {
+ $this->meta['card_type'] = $type;
+ }
+
+ /**
+ * Returns the card expiration year (YYYY).
+ * @since 2.6.0
+ * @return string Expiration year
+ */
+ public function get_expiry_year() {
+ return isset( $this->meta['expiry_year'] ) ? $this->meta['expiry_year'] : null;
+ }
+
+ /**
+ * Set the expiration year for the card (YYYY format).
+ * @since 2.6.0
+ * @param string $year
+ */
+ public function set_expiry_year( $year ) {
+ $this->meta['expiry_year'] = $year;
+ }
+
+ /**
+ * Returns the card expiration month (MM).
+ * @since 2.6.0
+ * @return string Expiration month
+ */
+ public function get_expiry_month() {
+ return isset( $this->meta['expiry_month'] ) ? $this->meta['expiry_month'] : null;
+ }
+
+ /**
+ * Set the expiration month for the card (MM format).
+ * @since 2.6.0
+ * @param string $month
+ */
+ public function set_expiry_month( $month ) {
+ $this->meta['expiry_month'] = $month;
+ }
+
+ /**
+ * Returns the last four digits.
+ * @since 2.6.0
+ * @return string Last 4 digits
+ */
+ public function get_last4() {
+ return isset( $this->meta['last4'] ) ? $this->meta['last4'] : null;
+ }
+
+ /**
+ * Set the last four digits.
+ * @since 2.6.0
+ * @param string $last4
+ */
+ public function set_last4( $last4 ) {
+ $this->meta['last4'] = $last4;
+ }
+
+}
diff --git a/includes/payment-tokens/class-wc-payment-token-echeck.php b/includes/payment-tokens/class-wc-payment-token-echeck.php
new file mode 100644
index 00000000000..3b42a197e6e
--- /dev/null
+++ b/includes/payment-tokens/class-wc-payment-token-echeck.php
@@ -0,0 +1,61 @@
+meta['last4'] ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the last four digits.
+ * @since 2.6.0
+ * @return string Last 4 digits
+ */
+ public function get_last4() {
+ return isset( $this->meta['last4'] ) ? $this->meta['last4'] : null;
+ }
+
+ /**
+ * Set the last four digits.
+ * @since 2.6.0
+ * @param string $last4
+ */
+ public function set_last4( $last4 ) {
+ $this->meta['last4'] = $last4;
+ }
+
+}
diff --git a/includes/shortcodes/class-wc-shortcode-my-account.php b/includes/shortcodes/class-wc-shortcode-my-account.php
index e0a7ccd0e02..894ba97a885 100644
--- a/includes/shortcodes/class-wc-shortcode-my-account.php
+++ b/includes/shortcodes/class-wc-shortcode-my-account.php
@@ -344,4 +344,69 @@ class WC_Shortcode_My_Account {
}
}
+
+ /**
+ * Deletes a payment method from a users list and displays a message to the user
+ *
+ * @since 2.6
+ * @param int $id Payment Token ID
+ */
+ public static function delete_payment_method( $id ) {
+ $token = WC_Payment_Tokens::get( $id );
+
+ if ( is_null( $token ) ) {
+ wc_add_notice( __( 'Invalid payment method', 'woocommerce' ), 'error' );
+ woocommerce_account_payment_methods();
+ return false;
+ }
+
+ if ( get_current_user_id() !== $token->get_user_id() ) {
+ wc_add_notice( __( 'Invalid payment method', 'woocommerce' ), 'error' );
+ woocommerce_account_payment_methods();
+ return false;
+ }
+
+ if ( false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'delete-payment-method-' . $id ) ) {
+ wc_add_notice( __( 'Invalid payment method', 'woocommerce' ), 'error' );
+ woocommerce_account_payment_methods();
+ return false;
+ }
+
+ WC_Payment_Tokens::delete( $id );
+ wc_add_notice( __( 'Payment method deleted.', 'woocommerce' ) );
+ woocommerce_account_payment_methods();
+ }
+
+ /**
+ * Sets a payment method as default and displays a message to the user
+ *
+ * @since 2.6
+ * @param int $id Payment Token ID
+ */
+ public static function set_default_payment_method( $id ) {
+ $token = WC_Payment_Tokens::get( $id );
+
+ if ( is_null( $token ) ) {
+ wc_add_notice( __( 'Invalid payment method', 'woocommerce' ), 'error' );
+ woocommerce_account_payment_methods();
+ return false;
+ }
+
+ if ( get_current_user_id() !== $token->get_user_id() ) {
+ wc_add_notice( __( 'Invalid payment method', 'woocommerce' ), 'error' );
+ woocommerce_account_payment_methods();
+ return false;
+ }
+
+ if ( false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'set-default-payment-method-' . $id ) ) {
+ wc_add_notice( __( 'Invalid payment method', 'woocommerce' ), 'error' );
+ woocommerce_account_payment_methods();
+ return false;
+ }
+
+ WC_Payment_Tokens::set_users_default( $token->get_user_id(), intval( $id ) );
+ wc_add_notice( __( 'This payment method was successfully set as your default.', 'woocommerce' ) );
+ woocommerce_account_payment_methods();
+ }
+
}
diff --git a/includes/wc-account-functions.php b/includes/wc-account-functions.php
index a29ca1fb9eb..73a97685c51 100644
--- a/includes/wc-account-functions.php
+++ b/includes/wc-account-functions.php
@@ -208,3 +208,106 @@ function wc_get_account_payment_methods_columns() {
'actions' => ' ',
) );
}
+
+/**
+ * Get My Account > Payment methods types
+ *
+ * @since 2.6.0
+ * @return array
+ */
+function wc_get_account_payment_methods_types() {
+ return apply_filters( 'woocommerce_payment_methods_types', array(
+ 'cc' => __( 'Credit Card', 'woocommerce' ),
+ 'echeck' => __( 'eCheck', 'woocommerce' ),
+ ) );
+}
+
+/**
+ * Returns an array of a user's saved payments list for output on the account tab.
+ *
+ * @since 2.6
+ * @param array $list List of payment methods passed from wc_get_customer_saved_methods_list()
+ * @param int $customer_id The customer to fetch payment methods for
+ * @return array Filtered list of customers payment methods
+ */
+function wc_get_account_saved_payment_methods_list( $list, $customer_id ) {
+ $payment_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id );
+ foreach ( $payment_tokens as $payment_token ) {
+ $delete_url = wc_get_endpoint_url( 'delete-payment-method', $payment_token->get_id() );
+ $delete_url = wp_nonce_url( $delete_url, 'delete-payment-method-' . $payment_token->get_id() );
+ $set_default_url = wc_get_endpoint_url( 'set-default-payment-method', $payment_token->get_id() );
+ $set_default_url = wp_nonce_url( $set_default_url, 'set-default-payment-method-' . $payment_token->get_id() );
+
+ $type = strtolower( $payment_token->get_type() );
+ $list[ $type ][] = array(
+ 'method' => array(
+ 'gateway' => $payment_token->get_gateway_id(),
+ ),
+ 'expires' => esc_html__( 'N/A', 'woocommerce' ),
+ 'is_default' => $payment_token->is_default(),
+ 'actions' => array(
+ 'delete' => array(
+ 'url' => $delete_url,
+ 'name' => esc_html__( 'Delete', 'woocommerce' ),
+ ),
+ ),
+ );
+ $key = key( array_slice( $list[ $type ], -1, 1, true ) );
+
+ if ( ! $payment_token->is_default() ) {
+ $list[ $type ][$key]['actions']['default'] = array(
+ 'url' => $set_default_url,
+ 'name' => esc_html__( 'Make Default', 'woocommerce' ),
+ );
+ }
+
+ $list[ $type ][ $key ] = apply_filters( 'woocommerce_payment_methods_list_item', $list[ $type ][ $key ], $payment_token );
+ }
+ return $list;
+}
+
+add_filter( 'woocommerce_saved_payment_methods_list', 'wc_get_account_saved_payment_methods_list', 10, 2 );
+
+/**
+ * Controls the output for credit cards on the my account page.
+ *
+ * @since 2.6
+ * @param array $item Individual list item from woocommerce_saved_payment_methods_list
+ * @param WC_Payment_Token $payment_token The payment token associated with this method entry
+ * @return array Filtered item
+ */
+function wc_get_account_saved_payment_methods_list_item_cc( $item, $payment_token ) {
+ if ( 'cc' !== strtolower( $payment_token->get_type() ) ) {
+ return $item;
+ }
+
+ $card_type = $payment_token->get_card_type();
+ $item['method']['last4'] = $payment_token->get_last4();
+ $item['method']['brand'] = ( ! empty( $card_type ) ? ucfirst( $card_type ) : esc_html__( 'Credit Card', 'woocommerce' ) );
+ $item['expires'] = $payment_token->get_expiry_month() . '/' . substr( $payment_token->get_expiry_year(), -2 );
+
+ return $item;
+}
+
+add_filter( 'woocommerce_payment_methods_list_item', 'wc_get_account_saved_payment_methods_list_item_cc', 10, 2 );
+
+/**
+ * Controls the output for eChecks on the my account page.
+ *
+ * @since 2.6
+ * @param array $item Individual list item from woocommerce_saved_payment_methods_list
+ * @param WC_Payment_Token $payment_token The payment token associated with this method entry
+ * @return array Filtered item
+ */
+function wc_get_account_saved_payment_methods_list_item_echeck( $item, $payment_token ) {
+ if ( 'echeck' !== strtolower( $payment_token->get_type() ) ) {
+ return $item;
+ }
+
+ $item['method']['last4'] = $payment_token->get_last4();
+ $item['method']['brand'] = esc_html__( 'eCheck', 'woocommerce' );
+
+ return $item;
+}
+
+add_filter( 'woocommerce_payment_methods_list_item', 'wc_get_account_saved_payment_methods_list_item_echeck', 10, 2 );
diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php
index 084f64a9e0e..d5472ea4e79 100644
--- a/includes/wc-core-functions.php
+++ b/includes/wc-core-functions.php
@@ -936,6 +936,31 @@ function wc_get_shipping_zone( $package ) {
return WC_Shipping_Zones::get_zone_matching_package( $package );
}
+/**
+ * Get a nice name for credit card providers.
+ *
+ * @since 2.6.0
+ * @param string $type Provider Slug/Type
+ * @return string
+ */
+function wc_get_credit_card_type_label( $type ) {
+ // Normalize
+ $type = strtolower( $type );
+ $type = str_replace( '-', ' ', $type );
+ $type = str_replace( '_', ' ', $type );
+
+ $labels = apply_filters( 'wocommerce_credit_card_type_labels', array(
+ 'mastercard' => __( 'MasterCard', 'woocommerce' ),
+ 'visa' => __( 'Visa', 'woocommerce' ),
+ 'discover' => __( 'Discover', 'woocommerce' ),
+ 'american express' => __( 'American Express', 'woocommerce' ),
+ 'diners' => __( 'Diners', 'woocommerce' ),
+ 'jcb' => __( 'JCB', 'woocommerce' ),
+ ) );
+
+ return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucfirst( $type ) ) );
+}
+
/**
* Outputs a "back" link so admin screens can easily jump back a page.
*
diff --git a/includes/wc-template-hooks.php b/includes/wc-template-hooks.php
index 409952bc043..7ee27c013d7 100644
--- a/includes/wc-template-hooks.php
+++ b/includes/wc-template-hooks.php
@@ -250,3 +250,5 @@ add_action( 'woocommerce_account_edit-address_endpoint', 'woocommerce_account_ed
add_action( 'woocommerce_account_payment-methods_endpoint', 'woocommerce_account_payment_methods' );
add_action( 'woocommerce_account_add-payment-method_endpoint', 'woocommerce_account_add_payment_method' );
add_action( 'woocommerce_account_edit-account_endpoint', 'woocommerce_account_edit_account' );
+add_action( 'woocommerce_account_set-default-payment-method_endpoint', array( 'WC_Shortcode_My_Account', 'set_default_payment_method' ) );
+add_action( 'woocommerce_account_delete-payment-method_endpoint', array( 'WC_Shortcode_My_Account', 'delete_payment_method' ) );
diff --git a/templates/myaccount/form-add-payment-method.php b/templates/myaccount/form-add-payment-method.php
index acfd3b114f1..af8580f8c5e 100644
--- a/templates/myaccount/form-add-payment-method.php
+++ b/templates/myaccount/form-add-payment-method.php
@@ -54,7 +54,7 @@ wc_get_template( 'myaccount/navigation.php' ); ?>
-
+
diff --git a/templates/myaccount/payment-methods.php b/templates/myaccount/payment-methods.php
index 6b005b561ac..dd955e882a8 100644
--- a/templates/myaccount/payment-methods.php
+++ b/templates/myaccount/payment-methods.php
@@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
$saved_methods = wc_get_customer_saved_methods_list( get_current_user_id() );
$has_methods = (bool) $saved_methods;
-
+$types = wc_get_account_payment_methods_types();
wc_print_notices(); ?>
@@ -43,27 +43,43 @@ wc_print_notices(); ?>
-
-
- $column_name ) : ?>
-
-
-
-
-
+ $methods ) : ?>
+
+
+ $column_name ) : ?>
+
+ $action ) {
+ echo '' . esc_html( $action['name'] ) . ' ';
+ }
+ }
+ ?>
+
+
+
+
-
+
-
+
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 89667360371..83521aba1a7 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -91,6 +91,7 @@ class WC_Unit_Tests_Bootstrap {
// framework
require_once( $this->tests_dir . '/framework/class-wc-unit-test-factory.php' );
require_once( $this->tests_dir . '/framework/class-wc-mock-session-handler.php' );
+ require_once( $this->tests_dir . '/framework/class-wc-payment-token-stub.php' );
// test cases
require_once( $this->tests_dir . '/framework/class-wc-unit-test-case.php' );
@@ -104,6 +105,7 @@ class WC_Unit_Tests_Bootstrap {
require_once( $this->tests_dir . '/framework/helpers/class-wc-helper-customer.php' );
require_once( $this->tests_dir . '/framework/helpers/class-wc-helper-order.php' );
require_once( $this->tests_dir . '/framework/helpers/class-wc-helper-shipping-zones.php' );
+ require_once( $this->tests_dir . '/framework/helpers/class-wc-helper-payment-token.php' );
}
/**
diff --git a/tests/framework/class-wc-payment-token-stub.php b/tests/framework/class-wc-payment-token-stub.php
new file mode 100644
index 00000000000..a24e2d7fa67
--- /dev/null
+++ b/tests/framework/class-wc-payment-token-stub.php
@@ -0,0 +1,26 @@
+meta['extra'] ) ? $this->meta['extra'] : '';
+ }
+
+ /**
+ * Set meta
+ * @param string $extra
+ */
+ public function set_extra( $extra ) {
+ $this->meta['extra'] = $extra;
+ }
+}
diff --git a/tests/framework/helpers/class-wc-helper-payment-token.php b/tests/framework/helpers/class-wc-helper-payment-token.php
new file mode 100644
index 00000000000..44cedf922e2
--- /dev/null
+++ b/tests/framework/helpers/class-wc-helper-payment-token.php
@@ -0,0 +1,56 @@
+set_last4( 1234 );
+ $token->set_expiry_month( '08' );
+ $token->set_expiry_year( '2016' );
+ $token->set_card_type( 'visa' );
+ $token->set_token( time() );
+ $token->save();
+ return $token;
+ }
+
+ /**
+ * Create a new eCheck payment token
+ *
+ * @since 2.6
+ * @return WC_Payment_Token_eCheck object
+ */
+ public static function create_eCheck_token() {
+ $token = new WC_Payment_Token_eCheck();
+ $token->set_last4( 1234 );
+ $token->set_token( time() );
+ $token->save();
+ return $token;
+ }
+
+ /**
+ * Create a new 'stub' payment token
+ *
+ * @since 2.6
+ * @param string $extra A string to insert and get to test the metadata functionality of a token
+ * @return WC_Payment_Token_Stub object
+ */
+ public static function create_stub_token( $extra ) {
+ $token = new WC_Payment_Token_Stub();
+ $token->set_extra( $extra );
+ $token->set_token( time() );
+ $token->save();
+ return $token;
+ }
+
+}
+
diff --git a/tests/unit-tests/order/functions.php b/tests/unit-tests/order/functions.php
index 9d62232d0ff..46f689c41ac 100644
--- a/tests/unit-tests/order/functions.php
+++ b/tests/unit-tests/order/functions.php
@@ -123,4 +123,36 @@ class Functions extends \WC_Unit_Test_Case {
// Assert the return when $the_order args is a random (incorrect) id.
$this->assertFalse( wc_get_order( 123456 ) );
}
+
+ /**
+ * Test getting an orders payment tokens
+ *
+ * @since 2.6
+ */
+ public function test_wc_order_get_payment_tokens() {
+ $order = \WC_Helper_Order::create_order();
+ $this->assertEmpty( $order->get_payment_tokens() );
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ update_post_meta( $order->id, '_payment_tokens', array( $token->get_id() ) );
+
+ $this->assertCount( 1, $order->get_payment_tokens() );
+ }
+
+
+ /**
+ * Test adding a payment token to an order
+ *
+ * @since 2.6
+ */
+ public function test_wc_order_add_payment_token() {
+ $order = \WC_Helper_Order::create_order();
+ $this->assertEmpty( $order->get_payment_tokens() );
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $order->add_payment_token( $token );
+
+ $this->assertCount( 1, $order->get_payment_tokens() );
+ }
+
}
diff --git a/tests/unit-tests/payment-tokens/cc.php b/tests/unit-tests/payment-tokens/cc.php
new file mode 100644
index 00000000000..494b0e88dcf
--- /dev/null
+++ b/tests/unit-tests/payment-tokens/cc.php
@@ -0,0 +1,138 @@
+set_token( time() . ' ' . __FUNCTION__ );
+ $this->assertFalse( $token->validate() );
+ $token->set_last4( '1111' );
+ $token->set_expiry_year( '2016' );
+ $token->set_expiry_month( '08' );
+ $token->set_card_type( 'visa' );
+ $this->assertTrue( $token->validate() );
+ }
+
+ /**
+ * Test validation for expiry length.
+ * @since 2.6.0
+ */
+ function test_wc_payment_token_cc_validate_expiry_length() {
+ $token = new \WC_Payment_Token_CC( 1 );
+ $token->set_token( time() . ' ' . __FUNCTION__ );
+ $this->assertFalse( $token->validate() );
+
+ $token->set_last4( '1111' );
+ $token->set_expiry_year( '16' );
+ $token->set_expiry_month( '08' );
+ $token->set_card_type( 'visa' );
+
+ $this->assertFalse( $token->validate() );
+
+ $token->set_expiry_year( '2016' );
+ $this->assertTrue( $token->validate() );
+
+ $token->set_expiry_month( '8' );
+ $this->assertFalse( $token->validate() );
+ }
+
+ /**
+ * Test getting a card type.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_get_card_type() {
+ $token = new \WC_Payment_Token_CC( 1, array(), array( 'card_type' => 'mastercard' ) );
+ $this->assertEquals( 'mastercard', $token->get_card_type() );
+ }
+
+ /**
+ * Test setting a token's card type.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_set_card_type() {
+ $token = new \WC_Payment_Token_CC( 1 );
+ $token->set_card_type( 'visa' );
+ $this->assertEquals( 'visa', $token->get_card_type() );
+ }
+
+ /**
+ * Test getting expiry year.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_get_expiry_year() {
+ $token = new \WC_Payment_Token_CC( 1, array(), array( 'expiry_year' => '2016' ) );
+ $this->assertEquals( '2016', $token->get_expiry_year() );
+ }
+
+ /**
+ * Test setting a token's expiry year.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_set_expiry_year() {
+ $token = new \WC_Payment_Token_CC( 1 );
+ $token->set_expiry_year( '2016' );
+ $this->assertEquals( '2016', $token->get_expiry_year() );
+ }
+
+ /**
+ * Test getting expiry month.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_get_expiry_month() {
+ $token = new \WC_Payment_Token_CC( 1, array(), array( 'expiry_month' => '08' ) );
+ $this->assertEquals( '08', $token->get_expiry_month() );
+ }
+
+ /**
+ * Test setting a token's expiry month.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_set_expiry_month() {
+ $token = new \WC_Payment_Token_CC( 1 );
+ $token->set_expiry_month( '08' );
+ $this->assertEquals( '08', $token->get_expiry_month() );
+ }
+
+ /**
+ * Test getting last4.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_get_last4() {
+ $token = new \WC_Payment_Token_CC( 1, array(), array( 'last4' => '1111' ) );
+ $this->assertEquals( '1111', $token->get_last4() );
+ }
+
+ /**
+ * Test setting a token's last4.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_cc_set_last4() {
+ $token = new \WC_Payment_Token_CC( 1 );
+ $token->set_last4( '2222' );
+ $this->assertEquals( '2222', $token->get_last4() );
+ }
+
+ /**
+ * Test reading/getting a token from DB correctly sets meta.
+ * @since 2.6.0
+ */
+ 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 );
+
+ $this->assertEquals( '1234', $token_read->get_last4() );
+ }
+
+}
diff --git a/tests/unit-tests/payment-tokens/echeck.php b/tests/unit-tests/payment-tokens/echeck.php
new file mode 100644
index 00000000000..3d8d0ac6ab1
--- /dev/null
+++ b/tests/unit-tests/payment-tokens/echeck.php
@@ -0,0 +1,55 @@
+set_token( time() . ' ' . __FUNCTION__ );
+ $this->assertFalse( $token->validate() );
+ $token->set_last4( '1111' );
+ $this->assertTrue( $token->validate() );
+ }
+
+ /**
+ * Test getting last4.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_echeck_get_last4() {
+ $token = new \WC_Payment_Token_eCheck( 1, array(), array( 'last4' => '1111' ) );
+ $this->assertEquals( '1111', $token->get_last4() );
+ }
+
+ /**
+ * Test setting a token's last4.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_echeck_set_last4() {
+ $token = new \WC_Payment_Token_eCheck( 1 );
+ $token->set_last4( '2222' );
+ $this->assertEquals( '2222', $token->get_last4() );
+ }
+
+ /**
+ * Test reading/getting a token from DB correctly sets meta.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_echeck_read_pulls_meta() {
+ $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 );
+
+ $this->assertEquals( '1234', $token_read->get_last4() );
+ }
+
+}
diff --git a/tests/unit-tests/payment-tokens/payment-token.php b/tests/unit-tests/payment-tokens/payment-token.php
new file mode 100644
index 00000000000..44c61013c78
--- /dev/null
+++ b/tests/unit-tests/payment-tokens/payment-token.php
@@ -0,0 +1,216 @@
+assertEquals( 1, $token->get_id() );
+ }
+
+ /**
+ * Test get type returns the class name/type.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_get_type() {
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $this->assertEquals( 'stub', $token->get_type() );
+ }
+
+ /**
+ * Test get token to make sure it returns the passed token.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_get_token() {
+ $raw_token = time() . ' ' . __FUNCTION__;
+ $token = new \WC_Payment_Token_Stub( 1, array( 'token' => $raw_token ) );
+ $this->assertEquals( $raw_token, $token->get_token() );
+ }
+
+ /**
+ * Test set token to make sure it sets the pased token.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_set_token() {
+ $raw_token = time() . ' ' . __FUNCTION__;
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $token->set_token( $raw_token );
+ $this->assertEquals( $raw_token, $token->get_token() );
+ }
+
+ /**
+ * Test get user ID to make sure it passes the correct ID.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_get_user_id() {
+ $token = new \WC_Payment_Token_Stub( 1, array( 'user_id' => 1 ) );
+ $this->assertEquals( 1, $token->get_user_id() );
+ }
+
+ /**
+ * Test get user ID to make sure it returns 0 if there is no user ID.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_get_user_id_defaults_to_0() {
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $this->assertEquals( 0, $token->get_user_id() );
+ }
+
+ /**
+ * Test set user ID to make sure it passes the correct ID.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_set_user_id() {
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $token->set_user_id( 5 );
+ $this->assertEquals( 5, $token->get_user_id() );
+ }
+
+ /**
+ * Test getting the gateway ID.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_get_gateway_id() {
+ $token = new \WC_Payment_Token_Stub( 1, array( 'gateway_id' => 'paypal' ) );
+ $this->assertEquals( 'paypal', $token->get_gateway_id() );
+ }
+
+ /**
+ * Test set the gateway ID.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_set_gateway_id() {
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $token->set_gateway_id( 'paypal' );
+ $this->assertEquals( 'paypal', $token->get_gateway_id() );
+ }
+
+ /**
+ * Test setting a token as default.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_set_default() {
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $token->set_default( true );
+ $this->assertTrue( $token->is_default() );
+ $token->set_default( false );
+ $this->assertFalse( $token->is_default() );
+ }
+
+ /**
+ * Test is_default.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_is_default_returns_correct_state() {
+ $token = new \WC_Payment_Token_Stub( 1, array( 'is_default' => true ) );
+ $this->assertTrue( $token->is_default() );
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $this->assertFalse( $token->is_default() );
+ $token = new \WC_Payment_Token_Stub( 1, array( 'is_default' => false ) );
+ $this->assertFalse( $token->is_default() );
+ }
+
+ /**
+ * Test that get_data returns the correct internal representation for a token.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_get_data() {
+ $raw_token = time() . ' ' . __FUNCTION__;
+ $token = new \WC_Payment_Token_Stub( 1, array(
+ 'token' => $raw_token,
+ 'gateway_id' => 'paypal'
+ ) );
+ $token->set_extra( 'woocommerce' );
+
+ $data = $token->get_data();
+
+ $this->assertEquals( $raw_token, $data['token'] );
+ $this->assertEquals( 'paypal', $data['gateway_id'] );
+ $this->assertEquals( 'stub', $data['type'] );
+ $this->assertEquals( 'woocommerce', $data['meta']['extra'] );
+ }
+
+ /**
+ * Test token validation.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_validation() {
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $token->set_token( time() . ' ' . __FUNCTION__ );
+ $this->assertTrue( $token->validate() );
+
+ $token = new \WC_Payment_Token_Stub( 1 );
+ $this->assertFalse( $token->validate() );
+ }
+
+ /**
+ * Test reading a token from the database.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_read() {
+ $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->get_token(), $token_read->get_token() );
+ $this->assertEquals( $token->get_extra(), $token_read->get_extra() );
+ }
+
+ /**
+ * Test updating a token.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_update() {
+ $token = \WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
+ $this->assertEquals( __FUNCTION__, $token->get_extra() );
+ $token->set_extra( ':)' );
+ $token->update();
+ $this->assertEquals( ':)', $token->get_extra() );
+ }
+
+ /**
+ * Test creating a new token.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_create() {
+ $token = new \WC_Payment_Token_Stub();
+ $token->set_extra( __FUNCTION__ );
+ $token->set_token( time() );
+ $token->create();
+
+ $this->assertNotEmpty( $token->get_id() );
+ $this->assertEquals( __FUNCTION__, $token->get_extra() );
+ }
+
+ /**
+ * Test deleting a token.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_delete() {
+ $token = \WC_Helper_Payment_Token::create_stub_token( __FUNCTION__ );
+ $token_id = $token->get_id();
+ $token->delete();
+ $get_token = \WC_Payment_Tokens::get( $token_id );
+ $this->assertNull( $get_token );
+ }
+
+ /**
+ * Test a meta function (like CC's last4) doesn't work on the core abstract class.
+ * @since 2.6.0
+ */
+ public function test_wc_payment_token_last4_doesnt_work() {
+ $token = new \WC_Payment_Token_Stub();
+ $this->assertFalse( is_callable( $token, 'get_last4' ) );
+ }
+
+}
diff --git a/tests/unit-tests/payment-tokens/payment-tokens.php b/tests/unit-tests/payment-tokens/payment-tokens.php
new file mode 100644
index 00000000000..8ceb1f30fe1
--- /dev/null
+++ b/tests/unit-tests/payment-tokens/payment-tokens.php
@@ -0,0 +1,177 @@
+assertEmpty( \WC_Payment_Tokens::get_order_tokens( $order->id ) );
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ update_post_meta( $order->id, '_payment_tokens', array( $token->get_id() ) );
+
+ $this->assertCount( 1, \WC_Payment_Tokens::get_order_tokens( $order->id ) );
+
+ }
+
+ /**
+ * Test getting tokens associated with a user and no gateway ID.
+ * @since 2.6.0
+ */
+ function test_wc_payment_tokens_get_customer_tokens_no_gateway() {
+ $this->assertEmpty( \WC_Payment_Tokens::get_customer_tokens( 1 ) );
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->save();
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->save();
+
+ $this->assertCount( 2, \WC_Payment_Tokens::get_customer_tokens( 1 ) );
+ }
+
+ /**
+ * Test getting tokens associated with a user and for a specific gateway.
+ * @since 2.6.0
+ */
+ function test_wc_payment_tokens_get_customer_tokens_with_gateway() {
+ $this->assertEmpty( \WC_Payment_Tokens::get_customer_tokens( 1 ) );
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->set_gateway_id( 'simplify_commerce' );
+ $token->save();
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->set_gateway_id( 'paypal' );
+ $token->save();
+
+ $this->assertCount( 2, \WC_Payment_Tokens::get_customer_tokens( 1 ) );
+ $this->assertCount( 1, \WC_Payment_Tokens::get_customer_tokens( 1, 'simplify_commerce' ) );
+
+ foreach ( \WC_Payment_Tokens::get_customer_tokens( 1, 'simplify_commerce' ) as $simplify_token ) {
+ $this->assertEquals( 'simplify_commerce', $simplify_token->get_gateway_id() );
+ }
+ }
+
+ /**
+ * Test getting a customers default token.
+ * @since 2.6.0
+ */
+ function test_wc_get_customer_default_token() {
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->set_gateway_id( 'simplify_commerce' );
+ $token->save();
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->set_default( true );
+ $token->set_gateway_id( 'paypal' );
+ $token->save();
+
+ $this->assertCount( 2, \WC_Payment_Tokens::get_customer_tokens( 1 ) );
+
+ $default_token = \WC_Payment_Tokens::get_customer_default_token( 1 );
+ $this->assertEquals( 'paypal', $default_token->get_gateway_id() );
+ }
+
+ /**
+ * Test getting a customers default token, when there is no default token.
+ * @since 2.6.0
+ */
+ function test_wc_get_customer_default_token_returns_null_when_no_default_token() {
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->set_gateway_id( 'simplify_commerce' );
+ $token->save();
+
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token->set_user_id( 1 );
+ $token->set_gateway_id( 'paypal' );
+ $token->save();
+
+ $this->assertCount( 2, \WC_Payment_Tokens::get_customer_tokens( 1 ) );
+
+ $default_token = \WC_Payment_Tokens::get_customer_default_token( 1 );
+ $this->assertNull( $default_token );
+ }
+
+ /**
+ * Test getting a token by ID.
+ * @since 2.6.0
+ */
+ function test_wc_payment_tokens_get() {
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token_id = $token->get_id();
+ $get_token = \WC_Payment_Tokens::get( $token_id );
+ $this->assertEquals( $token->get_token(), $get_token->get_token() );
+ }
+
+ /**
+ * Test deleting a token by ID.
+ * @since 2.6.0
+ */
+ function test_wc_payment_tokens_delete() {
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token_id = $token->get_id();
+
+ \WC_Payment_Tokens::delete( $token_id );
+
+ $get_token = \WC_Payment_Tokens::get( $token_id );
+ $this->assertNull( $get_token );
+ }
+
+ /**
+ * Test getting a token's type by ID.
+ * @since 2.6.0
+ */
+ function test_wc_payment_tokens_get_type_by_id() {
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token_id = $token->get_id();
+ $this->assertEquals( 'CC', \WC_Payment_Tokens::get_token_type_by_id( $token_id ) );
+ }
+
+ /**
+ * Test setting a users default token.
+ * @since 2.6.0
+ */
+ function test_wc_payment_tokens_set_users_default() {
+ $token = \WC_Helper_Payment_Token::create_cc_token();
+ $token_id = $token->get_id();
+ $token->set_user_id( 1 );
+ $token->save();
+
+ $token2 = \WC_Helper_Payment_Token::create_cc_token();
+ $token_id_2 = $token2->get_id();
+ $token2->set_user_id( 1 );
+ $token2->save();
+
+ $this->assertFalse( $token->is_default() );
+ $this->assertFalse( $token2->is_default() );
+
+ \WC_Payment_Tokens::set_users_default( 1, $token_id_2 );
+ $token->read( $token_id );
+ $token2->read( $token_id_2 );
+ $this->assertFalse( $token->is_default() );
+ $this->assertTrue( $token2->is_default() );
+
+ \WC_Payment_Tokens::set_users_default( 1, $token_id );
+ $token->read( $token_id );
+ $token2->read( $token_id_2 );
+ $this->assertTrue( $token->is_default() );
+ $this->assertFalse( $token2->is_default() );
+ }
+
+}
diff --git a/uninstall.php b/uninstall.php
index 146f2b42285..13f66193b94 100644
--- a/uninstall.php
+++ b/uninstall.php
@@ -50,6 +50,8 @@ if ( ! empty( $status_options['uninstall_data'] ) ) {
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_shipping_zone_locations" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_shipping_zones" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_sessions" );
+ $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_payment_tokens" );
+ $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_payment_tokenmeta" );
// Delete options.
$wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE 'woocommerce\_%';");
diff --git a/woocommerce.php b/woocommerce.php
index 37112aaad57..5d393eaf51c 100644
--- a/woocommerce.php
+++ b/woocommerce.php
@@ -167,6 +167,7 @@ final class WooCommerce {
add_action( 'init', array( $this, 'init' ), 0 );
add_action( 'init', array( 'WC_Shortcodes', 'init' ) );
add_action( 'init', array( 'WC_Emails', 'init_transactional_emails' ) );
+ add_action( 'init', array( $this, 'payment_token_metadata_wpdbfix' ), 0 );
}
/**
@@ -251,21 +252,25 @@ final class WooCommerce {
include_once( 'includes/class-wc-tracker.php' );
}
- $this->query = include( 'includes/class-wc-query.php' ); // The main query class
- $this->api = include( 'includes/class-wc-api.php' ); // API Class
+ $this->query = include( 'includes/class-wc-query.php' ); // The main query class
+ $this->api = include( 'includes/class-wc-api.php' ); // API Class
- include_once( 'includes/class-wc-auth.php' ); // Auth Class
- include_once( 'includes/class-wc-post-types.php' ); // Registers post types
- include_once( 'includes/abstracts/abstract-wc-product.php' ); // Products
- include_once( 'includes/abstracts/abstract-wc-order.php' ); // Orders
- include_once( 'includes/abstracts/abstract-wc-settings-api.php' ); // Settings API (for gateways, shipping, and integrations)
- include_once( 'includes/abstracts/abstract-wc-shipping-method.php' ); // A Shipping method
- include_once( 'includes/abstracts/abstract-wc-payment-gateway.php' ); // A Payment gateway
- include_once( 'includes/abstracts/abstract-wc-integration.php' ); // An integration with a service
- include_once( 'includes/class-wc-product-factory.php' ); // Product factory
- include_once( 'includes/class-wc-countries.php' ); // Defines countries and states
- include_once( 'includes/class-wc-integrations.php' ); // Loads integrations
- include_once( 'includes/class-wc-cache-helper.php' ); // Cache Helper
+ include_once( 'includes/class-wc-auth.php' ); // Auth Class
+ include_once( 'includes/class-wc-post-types.php' ); // Registers post types
+ include_once( 'includes/abstracts/abstract-wc-payment-token.php' ); // Payment Tokens
+ include_once( 'includes/abstracts/abstract-wc-product.php' ); // Products
+ include_once( 'includes/abstracts/abstract-wc-order.php' ); // Orders
+ include_once( 'includes/abstracts/abstract-wc-settings-api.php' ); // Settings API (for gateways, shipping, and integrations)
+ include_once( 'includes/abstracts/abstract-wc-shipping-method.php' ); // A Shipping method
+ include_once( 'includes/abstracts/abstract-wc-payment-gateway.php' ); // A Payment gateway
+ include_once( 'includes/abstracts/abstract-wc-integration.php' ); // An integration with a service
+ include_once( 'includes/class-wc-product-factory.php' ); // Product factory
+ include_once( 'includes/class-wc-payment-tokens.php' ); // Payment tokens controller
+ include_once( 'includes/gateways/class-wc-payment-gateway-cc.php' ); // CC Payment Gateway
+ include_once( 'includes/gateways/class-wc-payment-gateway-echeck.php' ); // eCheck Payment Gateway
+ include_once( 'includes/class-wc-countries.php' ); // Defines countries and states
+ include_once( 'includes/class-wc-integrations.php' ); // Loads integrations
+ include_once( 'includes/class-wc-cache-helper.php' ); // Cache Helper
if ( defined( 'WP_CLI' ) && WP_CLI ) {
include_once( 'includes/class-wc-cli.php' );
@@ -466,6 +471,15 @@ final class WooCommerce {
}
}
+ /**
+ * WooCommerce Payment Token Meta API - set table name
+ */
+ function payment_token_metadata_wpdbfix() {
+ global $wpdb;
+ $wpdb->payment_tokenmeta = $wpdb->prefix . 'woocommerce_payment_tokenmeta';
+ $wpdb->tables[] = 'woocommerce_payment_tokenmeta';
+ }
+
/**
* Get Checkout Class.
* @return WC_Checkout