Commit 4a30dde9 authored by Michael Iseard's avatar Michael Iseard
Browse files

Buckets of changes

parent 51e0dfc6
......@@ -123,6 +123,18 @@ class Kudos_Admin {
);
add_action( "admin_print_scripts-{$transactions_page_hook_suffix}", [$this, 'kudos_transactions_page_assets'] );
$subscriptions_page_hook_suffix = add_submenu_page(
'kudos-settings',
/* translators: %s: Plugin name */
sprintf(__('%s Subscriptions', 'kudos-donations'), 'Kudos'),
__('Subscriptions', 'kudos-donations'),
'manage_options',
'kudos-subscriptions',
[$this, 'subscriptions_table']
);
add_action( "admin_print_scripts-{$subscriptions_page_hook_suffix}", [$this, 'kudos_subscriptions_page_assets'] );
}
/**
......@@ -150,8 +162,22 @@ class Kudos_Admin {
* @since 1.1.0
*/
public function kudos_transactions_page_assets() {
wp_enqueue_style( $this->plugin_name . '-transactions', get_asset_url('kudos-admin-transactions.css'), [], $this->version, 'all' );
wp_enqueue_script( $this->plugin_name . '-transaction', get_asset_url('kudos-admin-transactions.js'), [ 'jquery' ], $this->version, false );
wp_enqueue_script( $this->plugin_name . '-transactions', get_asset_url('kudos-admin-transactions.js'), [ 'jquery' ], $this->version, false );
wp_localize_script($this->plugin_name . '-transactions', 'kudos', [
'confirmation' => __('Are you sure you want to delete this transaction?', 'kudos-donations'),
]);
}
/**
* Assets specific to the Kudos Subscriptions page
*
* @since 1.1.0
*/
public function kudos_subscriptions_page_assets() {
wp_enqueue_script( $this->plugin_name . '-subscriptions', get_asset_url('kudos-admin-subscriptions.js'), [ 'jquery' ], $this->version, false );
wp_localize_script($this->plugin_name . '-subscriptions', 'kudos', [
'confirmation' => __('Are you sure you want to cancel this subscription?', 'kudos-donations'),
]);
}
/**
......@@ -166,9 +192,9 @@ class Kudos_Admin {
if ('delete' === $table->current_action()) {
$message = __('Transaction deleted', 'kudos-donations');
} elseif ('bulk-delete' === $table->current_action() && isset($_REQUEST['bulk-delete'])) {
} elseif ('bulk-delete' === $table->current_action() && isset($_REQUEST['bulk-action'])) {
/* translators: %s: Number of transactions */
$message = sprintf(__('%s transaction(s) deleted', 'kudos-donations'), count($_REQUEST['bulk-delete']));
$message = sprintf(__('%s transaction(s) deleted', 'kudos-donations'), count($_REQUEST['bulk-action']));
}
?>
<div class="wrap">
......@@ -194,6 +220,46 @@ class Kudos_Admin {
<?php
}
/**
* Creates the subscriptions table
*
* @since 1.1.0
*/
public function subscriptions_table() {
$table = new Subscriptions_Table();
$table->prepare_items();
$message = '';
if ('cancel' === $table->current_action()) {
$message = __('Subscription cancelled', 'kudos-donations');
} elseif ('bulk-cancel' === $table->current_action() && isset($_REQUEST['bulk-action'])) {
/* translators: %s: Number of transactions */
$message = sprintf(__('%s subscription(s) cancelled', 'kudos-donations'), count($_REQUEST['bulk-action']));
}
?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php _e('Subscriptions', 'kudos-donations'); ?></h1>
<?php if (!empty($_REQUEST['s'])) { ?>
<span class="subtitle">
<?php
/* translators: %s: Search term */
printf(__('Search results for “%s”'), $_REQUEST['s'])
?>
</span>
<?php } ?>
<p><?php _e("Your recent Kudos subscriptions",'kudos-donations');?></p>
<?php if($message) { ?>
<div class="updated below-h2" id="message"><p><?php echo esc_html($message); ?></p></div>
<?php } ?>
<form id="subscriptions-table" method="POST">
<?php
$table->display();
?>
</form>
</div>
<?php
}
/**
* Exports transactions if request present.
* Needs to be hooked to admin_init as it modifies headers.
......@@ -205,9 +271,16 @@ class Kudos_Admin {
if(isset($_REQUEST['export_transactions'])) {
$table = new Transactions_Table();
$table->export_transactions();
$table->export();
}
if(isset($_REQUEST['export_subscriptions'])) {
$table = new Subscriptions_Table();
$table->export();
}
}
/**
......
......@@ -33,6 +33,7 @@ class Kudos_Activator {
Kudos_Logger::init();
self::create_transactions_table();
self::create_donors_table();
self::create_subscriptions_table();
self::set_defaults();
}
......@@ -50,14 +51,13 @@ class Kudos_Activator {
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
name VARCHAR(255),
email VARCHAR(320),
value DECIMAL(7,2) NOT NULL,
currency VARCHAR(255),
status VARCHAR(255) DEFAULT 'open' NOT NULL,
method VARCHAR(255),
mode VARCHAR(255) NOT NULL,
sequence_type VARCHAR(255) NOT NULL,
customer_id VARCHAR(255),
order_id VARCHAR(255) NOT NULL,
transaction_id VARCHAR(255),
PRIMARY KEY (id)
......@@ -87,7 +87,6 @@ class Kudos_Activator {
city VARCHAR(255),
country VARCHAR(255),
customer_id VARCHAR(255),
payment_frequency VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) $charset_collate";
......@@ -95,6 +94,35 @@ class Kudos_Activator {
dbDelta( $sql );
}
/**
* Creates the subscription table
*
* @since 1.1.0
*/
private static function create_subscriptions_table() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . "kudos_subscriptions"; //get the database table prefix to create my new table
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
value DECIMAL(7,2) NOT NULL,
currency VARCHAR(255),
frequency VARCHAR(255) NOT NULL,
customer_id VARCHAR(255),
transaction_id VARCHAR(255),
k_subscription_id VARCHAR(255),
subscription_id VARCHAR(255),
status VARCHAR(255),
PRIMARY KEY (id)
) $charset_collate";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
}
/**
* Adds default options if not already set
*
......
......@@ -4,28 +4,21 @@ namespace Kudos;
use wpdb;
class Kudos_Donor {
class Kudos_Donor extends Database_Object {
public const TABLE = "kudos_donors";
/**
* @var wpdb
*/
private $wpdb;
const TABLE = "kudos_donors";
protected $wpdb;
/**
* Kudos_Donor constructor.
*
* @since 1.1.0
* @var string
*/
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
}
protected $table;
/**
* @param string $email
* @param string $customer_id
* @param string $payment_frequency
* @param string $name
* @param string|null $street
* @param string|null $postcode
......@@ -35,65 +28,31 @@ class Kudos_Donor {
* @return bool|false|int
* @since 1.1.0
*/
public function create_donor($email, $customer_id, $payment_frequency, $name=null, $street=null, $postcode=null, $city=null, $country=null) {
return $this->wpdb->insert(
$this->wpdb->prefix . self::TABLE,
[
'email' => $email,
'name' => $name,
'street' => $street,
'postcode' => $postcode,
'city' => $city,
'country' => $country,
'customer_id' => $customer_id,
'payment_frequency' => $payment_frequency
]
);
public function insert_donor($email, $customer_id, $name=null, $street=null, $postcode=null, $city=null, $country=null) {
return $this->insert([
'email' => $email,
'name' => $name,
'street' => $street,
'postcode' => $postcode,
'city' => $city,
'country' => $country,
'customer_id' => $customer_id,
]);
}
/**
* Update donor by email
*
* @param string $email
* @param string $payment_frequency
* @param string $name
* @param string|null $street
* @param string|null $postcode
* @param string|null $city
* @param string|null $country
* @param array $array
*
* @return bool|false|int
* @since 1.1.0
*/
public function update_donor($email, $payment_frequency, $name=null, $street=null, $postcode=null, $city=null, $country=null) {
return $this->wpdb->update(
$this->wpdb->prefix . self::TABLE,
[
'name' => $name,
'street' => $street,
'postcode' => $postcode,
'city' => $city,
'country' => $country,
'payment_frequency' => $payment_frequency
], [
'email' => $email,
]
);
}
/**
* @param string $email // Email address used to find a donor
* @param array $fields // Specify fields to fetch from database
* @return false|int
*
* @return array|object|void|null
* @since 1.1.0
* * @since 1.1.0
*/
public function get_donor($email, array $fields=['*']) {
$wpdb = $this->wpdb;
$table = $this->wpdb->prefix . self::TABLE;
$columns = implode(', ', $fields);
public function update_donor($email, $array) {
return $wpdb->get_row( $wpdb->prepare( "
SELECT $columns FROM $table WHERE email = '%s'
", $email ) );
return $this->update($array, ['email' => $email]);
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
namespace Kudos;
use Composer\DependencyResolver\Transaction;
use Dompdf\Dompdf;
use Throwable;
......@@ -88,17 +89,12 @@ class Kudos_Invoice
'company_name' => get_option('_kudos_invoice_company_name'),
'company_address' => get_option('_kudos_invoice_company_address'),
'vat_number' => get_option('_kudos_invoice_vat_number'),
'donor_name' => $transaction->name,
'donor_street' => $transaction->street,
'donor_postcode' => $transaction->postcode,
'donor_city' => $transaction->city,
];
$donorClass = new Kudos_Donor();
$donor = $donorClass->get_donor($transaction->email);
if($donor) {
$invoiceArray['donor_name'] = $donor->name;
$invoiceArray['donor_street'] = $donor->street;
$invoiceArray['donor_postcode'] = $donor->postcode;
$invoiceArray['donor_city'] = $donor->city;
}
try {
$dompdf->loadHtml(
$twig->render('pdf/invoice.html.twig', $invoiceArray)
......@@ -154,14 +150,9 @@ class Kudos_Invoice
*/
public static function regenerate_invoices() {
global $wpdb;
$table = $wpdb->prefix . Kudos_Transaction::TABLE;
$query = "
SELECT * from $table
";
$transactions = new Kudos_Transaction();
$transactions = $transactions->get_transactions();
$transactions = $wpdb->get_results($query);
$n=0;
foreach ($transactions as $transaction) {
......
......@@ -4,7 +4,9 @@ namespace Kudos;
use Mollie\Api\Exceptions\ApiException;
use Mollie\Api\MollieApiClient;
use Mollie\Api\Resources\Customer;
use Mollie\Api\Resources\Payment;
use Mollie\Api\Resources\Subscription;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Request;
......@@ -104,23 +106,24 @@ class Kudos_Mollie
return false;
}
/**
* Creates a payment and returns it as an object
*
* @param $value
* @param string $redirectUrl
* @param string|null $name
* @param string|null $email
*
* @param $customerId
*
* @return bool|object
* @since 1.0.0
*/
public function create_payment($value, $redirectUrl, $name=null, $email=null, $customerId=null) {
/**
* Creates a payment and returns it as an object
*
* @param $value
* @param string $redirectUrl
* @param string|null $payment_frequency
* @param string|null $name
* @param string|null $email
*
* @param $customerId
*
* @return bool|object
* @since 1.0.0
*/
public function create_payment($value, $payment_frequency, $redirectUrl, $name=null, $email=null, $customerId=null) {
$mollieApi = $this->mollieApi;
$order_id = time();
$order_id = 'kdo_'.time();
$currency = 'EUR';
$value = number_format($value, 2);
......@@ -130,17 +133,36 @@ class Kudos_Mollie
$redirectUrl = add_query_arg('_wpnonce', wp_create_nonce('check_kudos_order-' . $order_id), $redirectUrl);
}
// Set payment frequency
switch ($payment_frequency) {
case "12 months":
$frequency_text = __('Yearly', 'kudos-donations');
$sequenceType = 'first';
break;
case "1 month":
$frequency_text = __('Monthly', 'kudos-donations');
$sequenceType = 'first';
break;
case "oneoff":
default:
$frequency_text = __('One off', 'kudos-donations');
$sequenceType = 'oneoff';
}
$paymentArray = [
"amount" => [
"currency" => $currency,
"value" => $value
],
"redirectUrl" => $redirectUrl,
"webhookUrl" => rest_url('kudos/v1/mollie/webhook'),
// "webhookUrl" => rest_url('kudos/v1/mollie/payment/webhook'),
"sequenceType" => $sequenceType,
"webhookUrl" => 'http://d38699244220.ngrok.io/wp-json/kudos/v1/mollie/payment/webhook',
/* translators: %s: The order id */
"description" => sprintf(__("Kudos Payment - %s", 'kudos-donations'), $order_id),
"description" => sprintf(__("Kudos Donation (%s) - %s", 'kudos-donations'), $frequency_text, $order_id),
'metadata' => [
'order_id' => $order_id,
'payment_frequency' => $payment_frequency,
'email' => $email,
'name' => $name
]
......@@ -155,7 +177,7 @@ class Kudos_Mollie
$payment = $mollieApi->payments->create($paymentArray);
$transaction = $this->transaction;
$transaction->create_transaction($order_id, $value, $currency, $payment->status, $payment->sequenceType, $email, $name);
$transaction->insert_transaction($order_id, $customerId, $value, $currency, $payment->status, $payment->sequenceType);
return $payment;
......@@ -166,6 +188,63 @@ class Kudos_Mollie
}
/**
* Create a subscription
*
* @param object $transaction
* @param $interval
* @param null $times
*
* @return bool|object
* @since 1.1.0
*/
public function create_subscription($transaction, $interval, $times=null) {
$mollieApi = $this->mollieApi;
$customer_id = $transaction->customer_id;
$k_subscription_id = 'kds_'.time();
$startDate = date("Y-m-d", strtotime("+" . $interval));
$currency = 'EUR';
$value = number_format($transaction->value, 2);
$subscriptionArray = [
"amount" => [
"value" => $value,
"currency" => $currency
],
"interval" => $interval,
"startDate" => $startDate,
"description" => sprintf(__('Kudos Subscription (%s) - %s', 'kudos-donations'), $interval, $k_subscription_id),
// "webhookUrl" => rest_url('kudos/v1/mollie/subscription/webhook'),
"webhookUrl" => 'http://d38699244220.ngrok.io/wp-json/kudos/v1/mollie/subscription/webhook',
"metadata" => [
"subscription_id" => $k_subscription_id
]
];
if($times) {
$subscriptionArray["times"] = $times;
}
try {
/** @var Customer $customer */
$customer = $mollieApi->customers->get($customer_id);
$subscription = $customer->createSubscription($subscriptionArray);
if($subscription) {
$kudos_subscription = new Kudos_Subscription();
$kudos_subscription->insert_subscription($transaction->transaction_id, $customer_id, $interval, $value, $currency, $k_subscription_id, $subscription->id, $subscription->status);
return $subscription;
}
return false;
} catch (ApiException $e) {
$this->logger->log($e->getMessage(), 'CRITICAL', [$customer_id, $subscriptionArray]);
return false;
}
}
/**
* @param $email
* @param $name
......@@ -194,6 +273,20 @@ class Kudos_Mollie
}
public function cancel_subscription($customerId, $subscriptionId) {
$mollieApi = $this->mollieApi;
try {
$customer = $mollieApi->customers->get($customerId);
return $customer->cancelSubscription($subscriptionId);
} catch (ApiException $e) {
$this->logger->log($e->getMessage(), 'CRITICAL', [$customerId, $subscriptionId]);
return false;
}
}
/**
* Register webhook using rest
*
......@@ -201,7 +294,9 @@ class Kudos_Mollie
* @return void
*/
public function register_webhook() {
register_rest_route( 'kudos/v1', 'mollie/webhook', [
// Payment webhook
register_rest_route( 'kudos/v1', 'mollie/payment/webhook', [
'methods' => 'POST',
'callback' => [$this, 'rest_api_mollie_webhook'],
'args' => [
......@@ -210,6 +305,17 @@ class Kudos_Mollie
]
]
] );
// Subscription webhook
register_rest_route( 'kudos/v1', 'mollie/subscription/webhook', [
'methods' => 'POST',
'callback' => [$this, 'rest_api_mollie_webhook'],
'args' => [
'id' => [
'required' => true
]
]
] );
}
/**
......@@ -307,27 +413,38 @@ class Kudos_Mollie
$order_id = $payment->metadata->order_id;
$transaction_id = $payment->id;
$status = $payment->status;
$this->transaction->update_transaction($order_id, $transaction_id, $status, $payment->method);
$sequence_type = $payment->sequenceType;
$this->transaction->update_transaction($order_id, [
'status' => $status,
'transaction_id' => $transaction_id,
'method' => $payment->method
]);
// Send email receipt on success
$mailer = new Kudos_Mailer();
if($payment->isPaid() && !$payment->hasRefunds() && !$payment->hasChargebacks()) {
// Get transaction
$transaction = $this->transaction->get_transaction($order_id);
$transaction = $this->transaction->get_transaction_by(['order_id' => $order_id]);
// Create invoice
$this->invoice->generate_invoice($transaction);
// Send email - email setting is checked in mailer
if($transaction->email) {
// Send email - email setting is checked in mailer
$mailer->send_invoice($transaction);
// Set up recurring payment if sequence is first
if($sequence_type === 'first') {
return $this->create_subscription($transaction, $payment->metadata->payment_frequency);
}
}
}
/* translators: %s: Mollie */
$note = sprintf(__( 'Webhook requested by %s.', 'kudos-donations' ),'Mollie');
$this->logger->log($note, 'INFO', ['order_id' => $order_id, 'status' => $status]);
$this->logger->log($note, 'INFO', ['order_id' => $order_id, 'status' => $status, 'sequence_type' => $sequence_type]);
return $response;
}
......
......@@ -4,79 +4,106 @@ namespace Kudos;
use wpdb;
class Kudos_Transaction {
class Kudos_Transaction extends Database_Object {
public const TABLE = "kudos_transactions";
/**
* @var wpdb
*/
private $wpdb;
const TABLE = "kudos_transactions";
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;