/** * Tests Services_Paymill_Clients->create() */ public function testCreate() { $email = '*****@*****.**'; $client = $this->_clients->create(array('email' => $email)); $this->assertArrayHasKey('email', $client); $this->assertEquals($email, $client['email']); return $client['id']; }
public function onAKPaymentCallback($paymentmethod, $data) { JLoader::import('joomla.utilities.date'); // Check if we're supposed to handle this if ($paymentmethod != $this->ppName) { return false; } $isValid = true; // Load the relevant subscription row $id = $data['sid']; $subscription = null; // CHECK: Is this a valid subscription record? if ($id > 0) { $subscription = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->setId($id)->getItem(); if ($subscription->akeebasubs_subscription_id <= 0 || $subscription->akeebasubs_subscription_id != $id) { $subscription = null; $isValid = false; } } else { $isValid = false; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'The subscription ID is invalid'; } // CHECK: Is the amount correct? $isPartialRefund = false; if ($isValid) { $mc_gross = $data['amount']; // Remember: the amount is in cents, e.g. 400 means 4.00 Euros $gross = (int) ($subscription->gross_amount * 100); $isValid = $gross - $mc_gross < 0.01; if (!$isValid) { $data['akeebasubs_failure_reason'] = 'Paid amount does not match the subscription amount'; } } // CHECK: Is this transaction valid? // Log the IPN data $this->logIPN($data, $isValid, 'CALLBACK'); // Fraud attempt? Do nothing more! if (!$isValid) { $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem(); $error_url = 'index.php?option=' . JRequest::getCmd('option') . '&view=level&slug=' . $level->slug . '&layout=' . JRequest::getCmd('layout', 'default'); $error_url = JRoute::_($error_url, false); JFactory::getApplication()->redirect($error_url, $data['akeebasubs_failure_reason'], 'error'); return false; } // ACTION: Initialise common variables if ($isValid) { $apiKey = $this->getPrivateKey(); $apiEndpoint = 'https://api.paymill.de/v2/'; $db = JFactory::getDbo(); } // CHECK: Do we have a user already defined in PayMill? $user = JFactory::getUser($subscription->user_id); $clientsObject = new Services_Paymill_Clients($apiKey, $apiEndpoint); $filters = array('email' => $user->email); $clients = $clientsObject->get($filters); // ACTION: Get the client ID or create and save a new user in PayMill if necessary if (count($clients)) { $clientRecord = array_pop($clients); } else { $params = array('email' => $user->email, 'description' => $user->name . ' [' . $user->username . ']'); try { $clientRecord = $clientsObject->create($params); } catch (Exception $exc) { $isValid = false; $params['akeebasubs_failure_reason'] = $exc->getMessage(); } if (!array_key_exists('id', $clientRecord) || empty($clientRecord['id'])) { // Apparently the client creation failed $isValid = false; $params['akeebasubs_failure_reason'] = JText::_('PLG_AKPAYMENT_PAYMILL_ERROR_CLIENT'); } // Log the user creation data $this->logIPN($data, $isValid, 'USER'); // Fraud attempt? Do nothing more! if (!$isValid) { $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem(); $error_url = 'index.php?option=' . JRequest::getCmd('option') . '&view=level&slug=' . $level->slug . '&layout=' . JRequest::getCmd('layout', 'default'); $error_url = JRoute::_($error_url, false); JFactory::getApplication()->redirect($error_url, $params['akeebasubs_failure_reason'], 'error'); return false; } } $client = $clientRecord['id']; // CHECK: Do we already have a payment for this subscription? // -- Load the processor key from database. This prevents race conditions. $query = $db->getQuery(true)->select($db->qn('processor_key'))->from('#__akeebasubs_subscriptions')->where($db->qn('akeebasubs_subscription_id') . ' = ' . $db->q($subscription->akeebasubs_subscription_id)); $db->setQuery($query); $payment_id = $db->loadResult(); // ACTION: Create and save a new payment for this subscription if there is no payment or transaction yet if (substr($payment_id, 0, 4) != 'pay_' && substr($payment_id, 0, 5) != 'tran_') { $params = array('client' => $client, 'token' => $data['token']); $paymentsObject = new Services_Paymill_Payments($apiKey, $apiEndpoint); try { $creditcard = $paymentsObject->create($params); } catch (Exception $exc) { $isValid = false; $params['akeebasubs_failure_reason'] = $exc->getMessage(); } if (!array_key_exists('id', $creditcard) || empty($creditcard['id'])) { // Apparently the credit card capture creation failed $isValid = false; $params['akeebasubs_failure_reason'] = JText::_('PLG_AKPAYMENT_PAYMILL_ERROR_CC') . '<br/>Tech info: <tt>' . htmlentities($creditcard['error']) . '</tt>'; } // Log the payment creation data $this->logIPN($data, $isValid, 'PAYMENT'); // Fraud attempt? Do nothing more! if (!$isValid) { $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem(); $error_url = 'index.php?option=' . JRequest::getCmd('option') . '&view=level&slug=' . $level->slug . '&layout=' . JRequest::getCmd('layout', 'default'); $error_url = JRoute::_($error_url, false); JFactory::getApplication()->redirect($error_url, $params['akeebasubs_failure_reason'], 'error'); return false; } $subscription->processor_key = $creditcard['id']; $payment_id = $creditcard['id']; // Save the payment information WITHOUT using the table (skips the plugins) // This prevents double payments from being recorded $oUpdate = (object) array('akeebasubs_subscription_id' => $subscription->akeebasubs_subscription_id, 'processor_key' => $subscription->processor_key, 'state' => 'P'); JFactory::getDbo()->updateObject('#__akeebasubs_subscriptions', $oUpdate, 'akeebasubs_subscription_id'); } // CHECK: Do we already have a transaction for this subscription? // -- Load the processor key from database. This prevents race conditions. $query = $db->getQuery(true)->select($db->qn('processor_key'))->from('#__akeebasubs_subscriptions')->where($db->qn('akeebasubs_subscription_id') . ' = ' . $db->q($subscription->akeebasubs_subscription_id)); $db->setQuery($query); $payment_id = $db->loadResult(); // ACTION: Create a transaction if necessary if (substr($payment_id, 0, 5) != 'tran_') { // First update the object with a fake transaction $subscription->processor_key = 'tran_in_progress'; // Save the payment information WITHOUT using the table (skips the plugins) // This prevents double payments from being recorded $oUpdate = (object) array('akeebasubs_subscription_id' => $subscription->akeebasubs_subscription_id, 'processor_key' => $subscription->processor_key, 'state' => 'P'); JFactory::getDbo()->updateObject('#__akeebasubs_subscriptions', $oUpdate, 'akeebasubs_subscription_id'); // Create the transaction $params = array('amount' => $data['amount'], 'currency' => $data['currency'], 'client' => $client, 'payment' => $payment_id, 'description' => $data['description']); try { $transactionsObject = new Services_Paymill_Transactions($apiKey, $apiEndpoint); $transaction = $transactionsObject->create($params); } catch (Exception $exc) { $isValid = false; $params['akeebasubs_failure_reason'] = $exc->getMessage(); } if (!array_key_exists('id', $transaction) || empty($transaction['id'])) { // Apparently the transaction creation failed $isValid = false; $params['akeebasubs_failure_reason'] = JText::_('PLG_AKPAYMENT_PAYMILL_ERROR_TRANS'); } // Log the payment creation data $this->logIPN($data, $isValid, 'TRANSACTION'); if (!$isValid) { $transaction_id = $payment_id; } else { $transaction_id = $transaction['id']; } // First update the object $subscription->processor_key = $transaction_id; // Save the payment information WITHOUT using the table (skips the plugins) // This prevents double payments from being recorded $oUpdate = (object) array('akeebasubs_subscription_id' => $subscription->akeebasubs_subscription_id, 'processor_key' => $subscription->processor_key); JFactory::getDbo()->updateObject('#__akeebasubs_subscriptions', $oUpdate, 'akeebasubs_subscription_id'); // Fraud attempt? Do nothing more! if (!$isValid) { $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem(); $error_url = 'index.php?option=' . JRequest::getCmd('option') . '&view=level&slug=' . $level->slug . '&layout=' . JRequest::getCmd('layout', 'default'); $error_url = JRoute::_($error_url, false); JFactory::getApplication()->redirect($error_url, $params['akeebasubs_failure_reason'], 'error'); return false; } } else { // ACTION: If no transaction is necessary, show an error $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem(); $error_url = 'index.php?option=' . JRequest::getCmd('option') . '&view=level&slug=' . $level->slug . '&layout=' . JRequest::getCmd('layout', 'default'); $error_url = JRoute::_($error_url, false); JFactory::getApplication()->redirect($error_url, 'Cannot process the transaction twice. Wait to receive your subscription confirmation email and do not retry submitting the payment form again.', 'error'); return false; } if ($isValid) { if ($this->params->get('sandbox') == $transaction['livemode']) { $isValid = false; $data['akeebasubs_failure_reason'] = "Transaction done in wrong mode."; } } // Payment status // Check the payment_status switch ($transaction['status']) { case 'closed': case 'partial_refunded': $newStatus = 'C'; break; case 'open': case 'pending': case 'preauthorize': $newStatus = 'P'; break; case 'failed': case 'refunded': $newStatus = 'X'; break; } // Update subscription status (this also automatically calls the plugins) $updates = array('akeebasubs_subscription_id' => $id, 'processor_key' => $transaction_id, 'state' => $newStatus, 'enabled' => 0); JLoader::import('joomla.utilities.date'); if ($newStatus == 'C') { $this->fixDates($subscription, $updates); } $subscription->save($updates); // Run the onAKAfterPaymentCallback events JLoader::import('joomla.plugin.helper'); JPluginHelper::importPlugin('akeebasubs'); $app = JFactory::getApplication(); $jResponse = $app->triggerEvent('onAKAfterPaymentCallback', array($subscription)); // Redirect the user to the "thank you" page $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem(); $thankyouUrl = JRoute::_('index.php?option=com_akeebasubs&view=message&slug=' . $level->slug . '&layout=order&subid=' . $subscription->akeebasubs_subscription_id, false); JFactory::getApplication()->redirect($thankyouUrl); return true; }