mollie_payment.module in Mollie Payment 7
Same filename and directory in other branches
Provides Mollie integration for the Payment platform.
File
mollie_payment.moduleView source
<?php
/**
* @file
* Provides Mollie integration for the Payment platform.
*/
define('MOLLIE_PAYMENT_RETURN_PATH', 'payment/mollie/return');
define('MOLLIE_PAYMENT_LISTENER_PATH', 'payment/mollie/listener');
define('MOLLIE_PAYMENT_RECURRING_LISTENER_PATH', 'payment/mollie/listener/recurring');
/**
* Implements hook_menu().
*/
function mollie_payment_menu() {
$items = array();
$items[MOLLIE_PAYMENT_RETURN_PATH] = array(
'page callback' => 'mollie_payment_return',
'page arguments' => array(
3,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items[MOLLIE_PAYMENT_LISTENER_PATH] = array(
'page callback' => 'mollie_payment_listener',
'page arguments' => array(
3,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items[MOLLIE_PAYMENT_RECURRING_LISTENER_PATH] = array(
'page callback' => 'mollie_payment_recurring_listener',
'page arguments' => array(
4,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_permission().
*/
function mollie_payment_permission() {
return array(
'administer mollie payment' => array(
'title' => t('Administer Mollie Payment'),
),
);
}
/**
* Implements hook_libraries_info().
*/
function mollie_payment_libraries_info() {
$libraries = array();
$libraries['mollie_api'] = array(
'name' => 'Mollie API client for PHP',
'vendor url' => 'https://www.mollie.nl/',
'download url' => 'https://github.com/mollie/mollie-api-php',
'version' => '1.9.1',
'files' => array(
'php' => array(
'src/Mollie/API/Autoloader.php',
),
),
);
return $libraries;
}
/**
* Implements hook_payment_method_controller_info().
*/
function mollie_payment_payment_method_controller_info() {
return array(
'MolliePaymentMethodController',
);
}
/**
* Implements hook_entity_load().
*/
function mollie_payment_entity_load(array $entities, $entity_type) {
if ($entity_type == 'payment_method') {
foreach ($entities as $payment_method) {
if ($payment_method->controller->name == 'MolliePaymentMethodController') {
$payment_method->controller_data = mollie_payment_payment_method_configuration_load($payment_method->pmid);
}
}
}
}
/**
* Implements hook_ENTITY_TYPE_ACTION().
*/
function mollie_payment_payment_method_presave(PaymentMethod $payment_method) {
$payment_method->module = 'mollie_payment';
}
/**
* Implements hook_ENTITY_TYPE_ACTION().
*/
function mollie_payment_payment_method_insert(PaymentMethod $payment_method) {
if ($payment_method->controller->name == 'MolliePaymentMethodController') {
mollie_payment_payment_method_configuration_save($payment_method->pmid, $payment_method->controller_data);
}
}
/**
* Implements hook_ENTITY_TYPE_ACTION().
*/
function mollie_payment_payment_method_update(PaymentMethod $payment_method) {
if ($payment_method->controller->name == 'MolliePaymentMethodController') {
mollie_payment_payment_method_configuration_save($payment_method->pmid, $payment_method->controller_data);
}
}
/**
* Implements hook_ENTITY_TYPE_ACTION().
*/
function mollie_payment_payment_method_delete(PaymentMethod $payment_method) {
if ($payment_method->controller->name == 'MolliePaymentMethodController') {
variable_del('mollie_payment_' . $payment_method->pmid . '_controller_data');
}
}
/**
* Implements hook_payment_recurring_can_OPERATION() for stop.
*/
function mollie_payment_payment_recurring_can_stop(Payment $payment) {
// Initialize the client.
$client = mollie_payment_get_client($payment);
if ($payment && $client) {
$customer_id = $payment->context_data['payment']['customer'];
$subscription_id = $payment->context_data['payment']['subscription'];
// Mollie API Client throws an exception when trying to get a cancelled
// subscription.
try {
$subscription = $client->customers_subscriptions
->withParentId($customer_id)
->get($subscription_id);
if (in_array($subscription->status, array(
'cancelled',
'completed',
))) {
return FALSE;
}
} catch (Exception $e) {
return FALSE;
}
}
return TRUE;
}
/**
* Implements hook_payment_recurring_OPERATION() for stop.
*/
function mollie_payment_payment_recurring_stop(Payment $payment) {
// Initialize the client.
$client = mollie_payment_get_client($payment);
if ($payment && $client) {
$customer_id = $payment->context_data['payment']['customer'];
$subscription_id = $payment->context_data['payment']['subscription'];
$subscription = $client->customers_subscriptions
->withParentId($customer_id)
->cancel($subscription_id);
drupal_set_message(t('The status of @subscription is @status.', array(
'@subscription' => $subscription->description,
'@status' => $subscription->status,
)));
}
}
/**
* Return callback.
*
* @param string $pid
* The id of the payment.
*
* Mollie is redirecting the visitor here after the payment process. At this
* point we don't know the status of the payment yet so we can only load
* the payment and call its finish callback.
*/
function mollie_payment_return($pid) {
// Load the Payment payment.
$payment = entity_load_single('payment', $pid);
// Fetch the Mollie payment id.
$id = $payment->context_data['payment']['id'];
// Initialize the client.
$client = mollie_payment_get_client($payment);
if ($payment && $client) {
// Load the Mollie payment.
$mollie_payment = $client->payments
->get($id);
// Update the status of the Payment payment.
mollie_payment_update_status($payment, $mollie_payment->status);
// At this moment this does not work in test mode.
if (!$payment->method->controller_data['test_mode'] && $mollie_payment->method) {
// Load the Mollie payment method.
$method = $client->methods
->get($mollie_payment->method);
// Store the Mollie payment method id and name.
$payment->context_data['payment']['method'] = $method->id;
$payment->context_data['payment']['method_name'] = $method->description;
entity_save('payment', $payment);
}
}
// Finish the Payment payment and hand control back over to the context.
$payment
->finish();
}
/**
* Listener callback.
*
* @param string $pid
* The id of the payment.
*
* Mollie calls this after the payment status has been changed. Mollie only
* gives us an id leaving us with the responsibility to get the payment status.
*/
function mollie_payment_listener($pid) {
// Load the Payment payment.
/** @var Payment $payment */
$payment = entity_load_single('payment', $pid);
// Fetch the Mollie payment id.
$parameters = drupal_get_query_parameters($_POST);
$id = $parameters['id'];
// Initialize the client.
$client = mollie_payment_get_client($payment);
if ($payment && $client) {
// Load the Mollie Payment.
$mollie_payment = $client->payments
->get($id);
// Update the status of the Payment payment.
mollie_payment_update_status($payment, $mollie_payment->status);
// Create a subscription once the mandate is pending or valid.
if (module_exists('payment_recurring')) {
$recurring_info = payment_recurring_recurring_payment_info($payment);
// There is no subscription yet.
if (!empty($recurring_info) && !isset($payment->context_data['payment']['subscription'])) {
// This is a recurring payment.
if (in_array($recurring_info['type'], array(
'subscription',
'installments',
))) {
$controller_data = $payment->method->controller_data;
$recurring_listener_path = MOLLIE_PAYMENT_RECURRING_LISTENER_PATH;
if (!empty($controller_data['webhook_base_url'])) {
$recurring_listener_path = $controller_data['webhook_base_url'] . '/' . $recurring_listener_path;
}
// Charge automatically.
$mandates = $client->customers_mandates
->withParentId($mollie_payment->customerId)
->all();
foreach ($mandates as $mandate) {
if (in_array($mandate->status, array(
'pending',
'valid',
))) {
// We are allowed to charge automatically.
$subscription_data = array(
'amount' => $payment
->totalAmount(TRUE),
'interval' => $recurring_info['interval'],
'description' => $payment->description,
'webhookUrl' => url($recurring_listener_path . '/' . $payment->pid, array(
'absolute' => TRUE,
)),
);
if ($recurring_info['type'] == 'installments' && isset($recurring_info['times'])) {
$subscription_data['times'] = $recurring_info['times'];
}
if (isset($recurring_info['startDate'])) {
$subscription_data['startDate'] = $recurring_info['startDate'];
}
$subscription = $client->customers_subscriptions
->withParentId($mollie_payment->customerId)
->create($subscription_data);
if ($subscription) {
$payment->context_data['payment']['customer'] = $mollie_payment->customerId;
$payment->context_data['payment']['subscription'] = $subscription->id;
entity_save('payment', $payment);
}
}
}
}
}
}
}
}
/**
* Recurring listener callback.
*
* @param string $pid
* The id of the payment.
*
* Mollie calls this after a new recurring payment was made or when the payment
* status has been changed. Mollie only gives us an id leaving us with the
* responsibility to get the payment status.
*/
function mollie_payment_recurring_listener($pid) {
// Load the Payment payment.
/** @var Payment $payment */
$payment = entity_load_single('payment', $pid);
// Fetch the Mollie payment id.
$parameters = drupal_get_query_parameters($_POST);
$id = $parameters['id'];
// Initialize the client.
$client = mollie_payment_get_client($payment);
if ($payment && $client) {
// Load the Mollie Payment.
$mollie_payment = $client->payments
->get($id);
if (module_exists('payment_recurring') && isset($mollie_payment->subscriptionId)) {
// This is a new payment for a subscription.
/** @var Payment $new_payment */
$new_payment = entity_create('payment', array(
'method' => $payment->method,
'currency_code' => 'EUR',
'amount' => $mollie_payment->amount,
'description' => $mollie_payment->description,
'recurring' => array(
'fpid' => $payment->pid,
),
'context_data' => array(
'payment' => array(
'id' => $mollie_payment->id,
'subscription' => $mollie_payment->subscriptionId,
),
),
));
$line_items = $payment
->getLineItems();
// Get the first line item to fetch the tax rate.
$line_item = reset($line_items);
$new_payment
->setLineItem(new PaymentLineItem(array(
'currency_code' => 'EUR',
'amount' => $mollie_payment->amount / (1 + $line_item->tax_rate),
'quantity' => 1,
'tax_rate' => $line_item->tax_rate,
'description' => $mollie_payment->description,
)));
entity_save('payment', $new_payment);
mollie_payment_update_status($new_payment, $mollie_payment->status);
}
}
}
/**
* Update status of payment.
*/
function mollie_payment_update_status(Payment $payment, $mollie_status) {
$payment_status = array(
'open' => PAYMENT_STATUS_PENDING,
'cancelled' => PAYMENT_STATUS_CANCELLED,
'pending' => PAYMENT_STATUS_PENDING,
'paid' => PAYMENT_STATUS_SUCCESS,
'paidout' => PAYMENT_STATUS_MONEY_TRANSFERRED,
'refunded' => PAYMENT_STATUS_CANCELLED,
'expired' => PAYMENT_STATUS_EXPIRED,
'failed' => PAYMENT_STATUS_FAILED,
'charged_back' => PAYMENT_STATUS_CANCELLED,
);
$payment
->setStatus(new PaymentStatusItem($payment_status[$mollie_status]));
entity_save('payment', $payment);
}
/**
* Payment method configuration form elements callback.
*
* @param array $form
* A Drupal form array.
* @param array $form_state
* The current state of the form.
*
* @return array
* A Drupal form array.
*/
function mollie_payment_configuration(array $form, array &$form_state) {
$controller_data = $form_state['payment']->method->controller_data;
if ($controller_data['advanced']) {
$payment = $form_state['payment'];
$client = mollie_payment_get_client($payment);
if ($client) {
if (module_exists('payment_recurring') && isset($payment->recurring)) {
// We can only use methods that support a first payment.
$methods_list = $client->methods
->all(0, 0, array(
'recurringType' => 'first',
));
}
else {
$methods_list = $client->methods
->all();
}
$methods = array();
foreach ($methods_list as $method) {
$methods[$method->id] = $method->description;
}
$form['mollie_payment_method'] = array(
'#type' => 'select',
'#title' => t('Method'),
'#options' => $methods,
);
// Do not bother users with a form element if there is nothing to choose.
if (count($methods) < 2) {
$form['mollie_payment_method']['#type'] = 'hidden';
reset($methods);
$form['mollie_payment_method']['#value'] = key($methods);
}
$issuer_list = $client->issuers
->all();
$issuers = array(
'' => t('Choose your issuer'),
);
foreach ($issuer_list as $issuer) {
$issuers[$issuer->id] = $issuer->name;
}
// Show the issuers but only if the selected method is 'ideal'.
$form['mollie_payment_issuer'] = array(
'#type' => 'select',
'#title' => t('Issuer'),
'#options' => $issuers,
'#states' => array(
'visible' => array(
':input[name="payment_method[payment_method_controller_payment_configuration][mollie_payment_method]"]' => array(
'value' => 'ideal',
),
),
),
);
}
}
if ($controller_data['test_mode']) {
$form['test_mode'] = array(
'#type' => 'markup',
'#markup' => theme('html_tag', array(
'element' => array(
'#tag' => 'pre',
'#value' => t('Mollie is in test mode. No real money is being transfered. Be sure to switch off test mode on production websites.'),
),
)),
);
}
return $form;
}
function mollie_payment_configuration_validate(array $form, array &$form_state) {
$values = drupal_array_get_nested_value($form_state['values'], $form['#parents']);
$form_state['payment']->method_data['mollie_payment_method'] = $values['mollie_payment_method'];
$form_state['payment']->method_data['mollie_payment_issuer'] = $values['mollie_payment_issuer'];
}
/**
* Payment method configuration form elements callback.
*
* @param array $form
* A Drupal form array.
* @param array $form_state
* The current state of the form.
*
* @return array
* A Drupal form array.
*/
function mollie_payment_method_configuration(array $form, array &$form_state) {
$controller_data = $form_state['payment_method']->controller_data;
if (!is_array($form)) {
$form = array();
}
/* @todo Use test and live ids, let user select test or live mode */
$form['mollie_api_key'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Mollie API key'),
'#description' => t('Your Mollie API key'),
'#default_value' => isset($controller_data['mollie_api_key']) ? $controller_data['mollie_api_key'] : '',
);
$form['mollie_test_api_key'] = array(
'#type' => 'textfield',
'#title' => t('Mollie test API key'),
'#description' => t('Your Mollie test API key'),
'#default_value' => isset($controller_data['mollie_test_api_key']) ? $controller_data['mollie_test_api_key'] : '',
);
$form['webhook_base_url'] = array(
'#type' => 'textfield',
'#title' => t('Webhook Base URL'),
'#description' => t('When testing in a local environment you may want to use a service like ngrok to
make your environment accessible from the outside. Leave empty for online environments. Please note
that Mollie checks the webhook URL\'s for a valid TLD. Mollie Payment does not work with a
local test domain like my-project.dev.'),
'#default_value' => isset($controller_data['webhook_base_url']) ? $controller_data['webhook_base_url'] : '',
);
$form['advanced'] = array(
'#type' => 'checkbox',
'#title' => t('Advanced'),
'#description' => t('In advanced mode the payer can select the payment method in Drupal.'),
'#default_value' => isset($controller_data['advanced']) ? $controller_data['advanced'] : 0,
);
$form['test_mode'] = array(
'#type' => 'checkbox',
'#title' => t('Test mode'),
'#description' => t('In test mode the test API key is used and no real money is transfered.'),
'#default_value' => isset($controller_data['test_mode']) ? $controller_data['test_mode'] : 0,
);
return $form;
}
/**
* Validation callback for payment method configuration form elements callback.
*
* @param array $form
* A Drupal form array.
* @param array $form_state
* The current state of the form.
*/
function mollie_payment_method_configuration_validate(array $form, array &$form_state) {
$values = drupal_array_get_nested_value($form_state['values'], $form['#parents']);
$form_state['payment_method']->controller_data['mollie_api_key'] = $values['mollie_api_key'];
$form_state['payment_method']->controller_data['mollie_test_api_key'] = $values['mollie_test_api_key'];
$form_state['payment_method']->controller_data['webhook_base_url'] = $values['webhook_base_url'];
$form_state['payment_method']->controller_data['advanced'] = $values['advanced'];
$form_state['payment_method']->controller_data['test_mode'] = $values['test_mode'];
}
/**
* Load the configuration from the database.
*/
function mollie_payment_payment_method_configuration_load($pmid) {
$config = db_select('mollie_payment_payment_method_configurations', 'c')
->fields('c', array(
'configuration',
))
->condition('pmid', $pmid, '=')
->execute()
->fetchField();
return unserialize($config);
}
/**
* Store the configuration in the database.
*/
function mollie_payment_payment_method_configuration_save($pmid, $config) {
db_merge('mollie_payment_payment_method_configurations')
->key(array(
'pmid' => $pmid,
))
->fields(array(
'configuration' => serialize($config),
))
->execute();
}
/**
* Initialize Mollie API client.
*/
function mollie_payment_get_client(Payment $payment) {
$api_key = mollie_payment_get_api_key($payment);
try {
libraries_load('mollie_api');
$client = new Mollie_API_Client();
$client
->setApiKey($api_key);
// Register major version of Drupal.
$client
->addVersionString('Drupal/7.x');
// Register minor version of Drupal.
$client
->addVersionString('Drupal/' . VERSION);
return $client;
} catch (Exception $e) {
drupal_set_message($e
->getMessage(), 'error');
return FALSE;
}
}
/**
* Get Mollie API key from payment.
*/
function mollie_payment_get_api_key(Payment $payment) {
$data = $payment->method->controller_data;
if ($data['test_mode']) {
return $data['mollie_test_api_key'];
}
return $data['mollie_api_key'];
}
Functions
Constants
Name | Description |
---|---|
MOLLIE_PAYMENT_LISTENER_PATH | |
MOLLIE_PAYMENT_RECURRING_LISTENER_PATH | |
MOLLIE_PAYMENT_RETURN_PATH | @file Provides Mollie integration for the Payment platform. |