Skip to content

Instantly share code, notes, and snippets.

@davdebcom
Last active February 11, 2020 15:14
Show Gist options
  • Select an option

  • Save davdebcom/c4ac989bfd9b9c3ad071ffcd056ee1b7 to your computer and use it in GitHub Desktop.

Select an option

Save davdebcom/c4ac989bfd9b9c3ad071ffcd056ee1b7 to your computer and use it in GitHub Desktop.
Mollie Payments & WooCommerce Subscriptions import
<?php
/**
* Import Subscriptions for WooCommerce 3.x, Subscriptions 2.x and Mollie Payments 5.x
*
* @author David de Boer <[email protected]>
* @license GPL-2.0+
* @link http://www.davdeb.com
*
*/
/**
* Include relevant parts of WordPress
*/
require_once( 'wp-load.php' );
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
/**
* Call davdebcom_import_get_csv_record() to get one subscription record
* Then call davdebcom_import_subscription_record() to process subscription
*/
$import = new DavdebcomImportSubscriptions();
$import->davdebcom_import_get_csv_record();
$import->davdebcom_import_subscription_record();
class DavdebcomImportSubscriptions {
private $key;
private $subscription;
private $mollie_api;
private $mollie_customer_id;
private $mollie_settings;
private $order;
private $user_id;
private $user_data;
private $import_status;
private $product;
// Mollie API mode, only change this manually, and only for production!
// Never test with this set to false! False means live mode, true means test mode.
private $mollie_test = true;
/**
* Get one subscription record from CSV file
*/
public function davdebcom_import_get_csv_record() {
// Don't start with 0, that's the header
$this->key = get_option( 'davdebcom_import_counter', '1' );
// If zero (error by user setting this in database) change to 1
if ( $this->key == '0' ) {
$this->key = 1;
}
if ( $this->key == 'completed' ) {
Mollie_WC_Plugin::debug( "Import of all subscriptions completed, nothing left to import." );
exit();
}
$import_file = 'subscriptions.csv';
if ( file_exists( $import_file ) == false ) {
echo "Import file -> $import_file <- not found!";
Mollie_WC_Plugin::debug( "Import file -> $import_file <- not found!" );
exit();
}
$subscriptions = array_map( 'str_getcsv', file( $import_file ) );
// On some servers, use:
// $subscriptions = array_map( 'str_getcsv', file( __DIR__.'/subscriptions.csv' ) );
if ( ! isset( $subscriptions[ $this->key ] ) || ! array_key_exists( $this->key, $subscriptions ) ) {
update_option( 'davdebcom_import_counter', 'completed' );
Mollie_WC_Plugin::debug( "No record left to import" );
exit();
}
//var_dump($subscriptions[0]);
//var_dump($subscriptions[$key]);
if ( array_combine( $subscriptions[0], $subscriptions[ $this->key ] ) !== false ) {
$this->subscription = array_combine( $subscriptions[0], $subscriptions[ $this->key ] );
} else {
echo "Combining arrays failed, number of elements not equal, probably because error in CSV file.";
Mollie_WC_Plugin::debug( "ERROR - Combining arrays failed, number of elements not equal, probably because error in CSV file." );
}
return true;
}
/**
* Start processing one subscription record
*/
public function davdebcom_import_subscription_record() {
//
// Validate subscription record
//
$this->davdebcom_import_validate_subscription_record();
//
// Get import status
//
$this->davdebcom_import_get_import_status();
//
// Setup Mollie API
//
$this->davdebcom_import_setup_mollie();
//
// Check for required information
//
$this->davdebcom_import_check_required_information();
//
// Setup WooCommerce product
//
$this->davdebcom_import_setup_product();
//
// Create WordPress user
//
$this->davdebcom_import_create_user();
//
// Create Mollie Customer and Mandate
//
$this->davdebcom_import_create_mollie_customer_mandate();
//
// Create order
//
$this->davdebcom_import_create_order();
//
// Create subscription
//
$this->davdebcom_import_create_subscription();
//
// Complete import of one record
//
$this->davdebcom_import_complete_single_subscription_import();
}
/**
* Validate that subscription record is not empty or not an array
* @return bool
*/
private function davdebcom_import_validate_subscription_record() {
if ( ! is_array( $this->subscription ) ) {
Mollie_WC_Plugin::debug( 'ERROR - Import failed, subscription record empty or not an array. Key: ' . $this->key );
return false;
} else {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Import started' );
}
return true;
}
/**
* Get status of current import
* @return bool
*/
private function davdebcom_import_get_import_status() {
$this->import_status = get_option( 'davdebcom_import_status', 'test' );
if ( $this->import_status == 'test' ) {
var_dump( $this->subscription );
}
return true;
}
/**
* Setup Mollie
* @return bool
*/
private function davdebcom_import_setup_mollie() {
$this->mollie_settings = new Mollie_WC_Helper_Settings;
$api_key = $this->mollie_settings->getApiKey( $this->mollie_test );
$this->mollie_api = Mollie_WC_Plugin::getApiHelper()->getApiClient( $this->mollie_test );
$this->mollie_api->setApiKey( $api_key );
return true;
}
/**
* Check for the required information
* @return bool
*/
private function davdebcom_import_check_required_information() {
//
// Check for billing period and billing interval in CSV record
// TODO: v2: Add a more complete check for minimum required information
//
if ( empty( $this->subscription['customer_email'] ) ) {
Mollie_WC_Plugin::debug( 'ERROR - No valid customer email!' );
$this->davdebcom_import_failed_single_subscription_import();
return false;
}
if ( empty( $this->subscription['billing_period'] ) || empty( $this->subscription['billing_interval'] ) ) {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - No billing_period or billing_interval in CSV data!' );
$this->davdebcom_import_failed_single_subscription_import();
return false;
}
if ( ( empty( $this->subscription['customer_iban'] ) || empty( $this->subscription['customer_bic'] ) ) ) {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - No valid customer IBAN and/or BIC found!' );
$this->davdebcom_import_failed_single_subscription_import();
return false;
}
return true;
}
/**
* Setup the correct WooCommerce product
* @return bool
*/
private function davdebcom_import_setup_product() {
$test_product = get_option( 'davdebcom_import_test_product', '' );
if ( ! empty( $test_product ) ) {
$this->product = wc_get_product( $test_product );
} else {
$this->product = wc_get_product( $this->subscription['product_id'] );
}
if ( $this->product === false ) {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - No product found with that ID: ' . $this->subscription['product_id'] );
$this->davdebcom_import_failed_single_subscription_import();
return false;
} else {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Subscription product: ' . $this->product->get_id() );
}
return true;
}
/**
* Create a WordPress user
* @return bool
*/
private function davdebcom_import_create_user() {
$order_email = '';
// Get the user email from the order
if ( $this->import_status == 'test' ) {
$order_email = 'email+' . microtime() . '@example.com';
}
if ( ! empty( $this->subscription['customer_email'] ) && $this->import_status == 'live' ) {
$order_email = $this->subscription['customer_email'];
}
// Check if there are any users with the billing email as username or email
$email = email_exists( $order_email );
// Create a user if email is null or false
if ( $email === false ) {
// random password with 12 chars
$random_password = wp_generate_password();
// create new user with email as username & newly created pw
// Decided with Maarten to use email as username in WordPress, not set first and last name
$this->user_id = wp_create_user( $order_email, $random_password, $order_email );
// User's shipping data
update_user_meta( $this->user_data->id, 'shipping_company', $this->subscription['shipping_company'] );
update_user_meta( $this->user_data->id, 'shipping_first_name', $this->subscription['shipping_first_name'] );
update_user_meta( $this->user_data->id, 'shipping_last_name', $this->subscription['shipping_last_name'] );
update_user_meta( $this->user_data->id, 'shipping_address_1', $this->subscription['shipping_address_1'] );
update_user_meta( $this->user_data->id, 'shipping_address_2', $this->subscription['shipping_address_2'] );
update_user_meta( $this->user_data->id, 'shipping_city', $this->subscription['shipping_city'] );
update_user_meta( $this->user_data->id, 'shipping_country', $this->subscription['shipping_country'] );
update_user_meta( $this->user_data->id, 'shipping_postcode', $this->subscription['shipping_postcode'] );
update_user_meta( $this->user_data->id, 'shipping_city', $this->subscription['shipping_city'] );
// User's billing data
update_user_meta( $this->user_data->id, 'billing_company', $this->subscription['billing_company'] );
update_user_meta( $this->user_data->id, 'billing_first_name', $this->subscription['billing_first_name'] );
update_user_meta( $this->user_data->id, 'billing_last_name', $this->subscription['billing_last_name'] );
update_user_meta( $this->user_data->id, 'billing_address_1', $this->subscription['billing_address_1'] );
update_user_meta( $this->user_data->id, 'billing_address_2', $this->subscription['billing_address_2'] );
update_user_meta( $this->user_data->id, 'billing_city', $this->subscription['billing_city'] );
update_user_meta( $this->user_data->id, 'billing_country', $this->subscription['billing_country'] );
update_user_meta( $this->user_data->id, 'billing_postcode', $this->subscription['billing_postcode'] );
update_user_meta( $this->user_data->id, 'billing_city', $this->subscription['billing_city'] );
wc_update_new_customer_past_orders( $this->user_data->id );
} else {
$this->user_id = email_exists( $order_email );
}
// Get WordPress user data
$this->user_data = get_userdata( $this->user_id );
//
// Add meta to user
//
foreach ( $this->subscription as $key => $val ) {
if ( strpos( $key, 'custom_user_meta_' ) !== false ) {
$post_meta_key = str_replace( 'custom_user_meta_', '', $key );
update_user_meta( $this->user_data->ID, $post_meta_key, $val );
}
}
return true;
}
/**
* Create a Mollie customer and mandate
* @return bool
*/
private function davdebcom_import_create_mollie_customer_mandate() {
//
// Create Mollie Customer
//
try {
// Only use email for customers at Mollie (name is optional)
$customer = $this->mollie_api->customers->create( array (
'email' => trim( $this->user_data->user_email ),
'metadata' => array ( 'user_id' => $this->user_data->id ),
) );
$this->mollie_customer_id = $customer->id;
update_user_meta( $this->user_data->id, 'mollie_customer_id', $this->mollie_customer_id );
}
catch ( Mollie\Api\Exceptions\ApiException $e ) {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - Customer not created.' );
Mollie_WC_Plugin::debug( "API call failed: " . htmlspecialchars( $e->getMessage() ) );
$this->davdebcom_import_failed_single_subscription_import();
return false;
}
//
// Format a correct full name for the mandate
//
// Generate an optimal name for the mandate
$user_full_name = $this->subscription['billing_first_name'] . ' ' . $this->subscription['billing_last_name'];
// If shipping_first_name and shipping_last_name don't provide a good name, try shipping_company
if ( strlen( trim( $user_full_name ) ) == null ) {
$user_full_name = $this->subscription['billing_company'];
}
// If shipping_company doesn't provide a good name, no mandate can be created
// Set payment method to manual
if ( strlen( trim( $user_full_name ) ) == null ) {
$this->subscription['payment_method'] = 'manual';
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - No correct name found, impossible to create mandate, switched payment method to manual.' );
return false;
}
//
// Create actual Mollie mandate
//
try {
if ( $this->import_status == 'test' ) {
$options = array (
"method" => 'directdebit',
"consumerName" => 'T. TEST',
"consumerAccount" => 'BE62510007547061',
"consumerBic" => 'GEBABEBB',
);
}
if ( $this->import_status == 'live' ) {
$options = array (
"method" => 'directdebit',
"consumerName" => $user_full_name,
"consumerAccount" => $this->subscription['customer_iban'],
"consumerBic" => $this->subscription['customer_bic'],
);
}
$customer = $this->mollie_api->customers->get( $this->mollie_customer_id );
$this->mollie_api->mandates->createFor( $customer, $options );
}
catch ( Mollie\Api\Exceptions\ApiException $e) {
Mollie_WC_Plugin::debug( "API call failed: " . htmlspecialchars( $e->getMessage() ) );
return false;
}
//
// Verify the created mandate
//
$mandates = $this->mollie_api->customers->get( $this->mollie_customer_id );
if ( $mandates->hasValidMandate() ) {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': A valid mandate was found.' );
} else {
$this->subscription['payment_method'] = 'manual';
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - No valid mandate found, switched payment method to manual.' );
return false;
}
return true;
}
/**
* Create the WooCommerce order
* @return array|mixed|\WC_Order|\WP_Error
*/
private function davdebcom_import_create_order() {
// Create a new order in WooCommerce
$this->order = wc_create_order( array ( 'customer_id' => $this->user_data->id ) );
// Check for product quantity and otherwise set to 1
$this->subscription['product_quantity'] = ( ! empty ( $this->subscription['product_quantity'] ) ) ? $this->subscription['product_quantity'] : '1' ;
// Add product to order with quantity 1
$this->order->add_product( $this->product, $this->subscription['product_quantity'] );
// Create shipping rate if shipping costs are set in subscription record
if ( ! empty( $this->subscription['order_shipping'] ) && $this->subscription['order_shipping'] != 0 ) {
$shipping_rate = new WC_Shipping_Rate( '', 'Verzendkosten',
$this->subscription['order_shipping'], '21',
'custom_shipping_method' );
$this->order->add_shipping( $shipping_rate );
}
// Calculate totals for the created order
$this->order->calculate_totals();
// Update status for order to completed
$this->order->update_status( 'completed', 'Imported order.', false );
$this->order->save();
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Order created with ID: ' . $this->order->get_id() );
// Add user's shipping data to order
update_post_meta( $this->order->get_id(), '_shipping_company', $this->subscription['shipping_company'] );
update_post_meta( $this->order->get_id(), '_shipping_first_name', $this->subscription['shipping_first_name'] );
update_post_meta( $this->order->get_id(), '_shipping_last_name', $this->subscription['shipping_last_name'] );
update_post_meta( $this->order->get_id(), '_shipping_address_1', $this->subscription['shipping_address_1'] );
update_post_meta( $this->order->get_id(), '_shipping_address_2', $this->subscription['shipping_address_2'] );
update_post_meta( $this->order->get_id(), '_shipping_city', $this->subscription['shipping_city'] );
update_post_meta( $this->order->get_id(), '_shipping_country', $this->subscription['shipping_country'] );
update_post_meta( $this->order->get_id(), '_shipping_postcode', $this->subscription['shipping_postcode'] );
update_post_meta( $this->order->get_id(), '_shipping_city', $this->subscription['shipping_city'] );
// Add user's billing data to order
update_post_meta( $this->order->get_id(), '_billing_company', $this->subscription['billing_company'] );
update_post_meta( $this->order->get_id(), '_billing_first_name', $this->subscription['billing_first_name'] );
update_post_meta( $this->order->get_id(), '_billing_last_name', $this->subscription['billing_last_name'] );
update_post_meta( $this->order->get_id(), '_billing_address_1', $this->subscription['billing_address_1'] );
update_post_meta( $this->order->get_id(), '_billing_address_2', $this->subscription['billing_address_2'] );
update_post_meta( $this->order->get_id(), '_billing_city', $this->subscription['billing_city'] );
update_post_meta( $this->order->get_id(), '_billing_country', $this->subscription['billing_country'] );
update_post_meta( $this->order->get_id(), '_billing_postcode', $this->subscription['billing_postcode'] );
update_post_meta( $this->order->get_id(), '_billing_city', $this->subscription['billing_city'] );
// Add Mollie customer ID (not WooCommerce) and payment mode to order
update_post_meta( $this->order->get_id(), '_mollie_customer_id', $this->mollie_customer_id );
update_post_meta( $this->order->get_id(), '_mollie_payment_mode', ( $this->mollie_test ) ? 'test' : 'live' );
update_post_meta( $this->order->get_id(), '_payment_method', 'mollie_wc_gateway_ideal' );
return true;
}
/**
* Create WooCommerce subscription
* @return bool
*/
private function davdebcom_import_create_subscription() {
// Make sure start time is in correct format
$this->subscription['start_date'] = new DateTime( $this->subscription['start_date'] );
$this->subscription['start_date'] = $this->subscription['start_date']->format( 'Y-m-d H:i:s' );
// Create a new subscription with basic details
$sub = wcs_create_subscription( array (
'order_id' => $this->order->get_id(),
'billing_period' => $this->subscription['billing_period'],
'billing_interval' => $this->subscription['billing_interval'],
'start_date' => gmdate( $this->subscription['start_date'] ),
) );
// Update next_payment_date in record if date is in the past
$start_date = new DateTime( $this->subscription['start_date'] );
$next_payment_date = new DateTime( $this->subscription['next_payment_date'] );
$now = new DateTime();
// Default (and convert to valid format)
$this->subscription['next_payment_date'] = $next_payment_date->format( 'Y-m-d H:i:s' );
// Adjust next payment date if in the past (and convert to valid format)
if ( $next_payment_date < $now ) {
echo "first";
$now->modify( '+7 days' );
$this->subscription['next_payment_date'] = $now->format( 'Y-m-d H:i:s' );
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Next payment date (' . $next_payment_date->format( 'Y-m-d' ) . ') updated to: ' . $this->subscription['next_payment_date'] );
}
// Adjust next payment date if same as start date and both in the future (and convert to valid format)
if ( ($start_date > $now) && ($start_date == $next_payment_date) ) {
echo "second";
$next_payment_date->modify( '+7 days' );
$this->subscription['next_payment_date'] = $next_payment_date->format( 'Y-m-d H:i:s' );
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Next payment date (' . $next_payment_date->format( 'Y-m-d' ) . ') updated to: ' . $this->subscription['next_payment_date'] );
}
echo $this->subscription['next_payment_date'];
// Update next payment in WooCommerce subscription
$sub->update_dates( array (
'next_payment' => gmdate( $this->subscription['next_payment_date'] ),
), 'site' );
if ( is_wp_error( $sub ) ) {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': ERROR - Creating subscription failed: ' . $sub->get_error_message() );
$this->davdebcom_import_failed_single_subscription_import();
return false;
}
// Create shipping rate
if ( ! empty( $this->subscription['order_shipping'] ) && $this->subscription['order_shipping'] != 0 ) {
$shipping_rate = new WC_Shipping_Rate( '', 'Verzendkosten',
$this->subscription['order_shipping'], '21',
'custom_shipping_method' );
$sub->add_shipping( $shipping_rate );
}
$sub->add_product( $this->product );
// Calculate totals for the created subscription
$sub->calculate_totals();
// Set the subscription's billing and shipping address
wcs_copy_order_address( $this->order, $sub, 'all' );
// Copy order meta data to subscription, just in case
wcs_copy_order_meta( $this->order, $sub, 'subscription' );
// Set the subscription's payment method
if ( $this->subscription['payment_method'] == 'manual' ) {
$sub->set_requires_manual_renewal( true );
} else {
$sub->set_requires_manual_renewal( false );
$sub->set_payment_method( 'mollie_wc_gateway_directdebit' );
}
$sub->save();
// Add Mollie Customer ID manually, just in case
update_post_meta( $sub->get_id(), '_payment_method', 'mollie_wc_gateway_ideal' );
update_post_meta( $sub->get_id(), '_requires_manual_renewal', 'false' );
update_post_meta( $sub->get_id(), '_mollie_customer_id', $this->mollie_customer_id );
// Make sure subscription is activated
WC_Subscriptions_Manager::activate_subscriptions_for_order( $this->order );
// Send a message to the log
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Subscription created with ID: ' . $sub->get_id() );
if ( $this->import_status == 'test' ) {
var_dump( $sub );
}
//
// Add custom meta to subscription
//
foreach ( $this->subscription as $key => $val ) {
if ( strpos( $key, 'custom_post_meta_' ) !== false ) {
$post_meta_key = str_replace( 'custom_post_meta_', '', $key );
update_post_meta( $sub->get_id(), $post_meta_key, $val );
}
}
return true;
}
/**
* Mark the subscription import as failed, increment key/counter so next subscription can be processed
* @return bool
*/
private function davdebcom_import_failed_single_subscription_import() {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . 'IMPORT FAILED - Stopped processing, skipping to next subscription. Failed ID: ' . $this->key );
$this->key = $this->key + 1;
update_option( 'davdebcom_import_counter', $this->key );
exit();
}
/**
* Mark the subscription import as complete and increment key/counter in database
* @return bool
*/
private function davdebcom_import_complete_single_subscription_import() {
Mollie_WC_Plugin::debug( $this->subscription['customer_email'] . ': Import completed' );
$this->key = $this->key + 1;
update_option( 'davdebcom_import_counter', $this->key );
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment