[WIP] TwingleDonation.Submit API action
This commit is contained in:
parent
f8575ac7ce
commit
061972706c
4 changed files with 978 additions and 0 deletions
278
CRM/Twingle/Profile.php
Normal file
278
CRM/Twingle/Profile.php
Normal file
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
/*------------------------------------------------------------+
|
||||
| SYSTOPIA Twingle Integration |
|
||||
| Copyright (C) 2018 SYSTOPIA |
|
||||
| Author: J. Schuppe (schuppe@systopia.de) |
|
||||
+-------------------------------------------------------------+
|
||||
| This program is released as free software under the |
|
||||
| Affero GPL license. You can redistribute it and/or |
|
||||
| modify it under the terms of this license which you |
|
||||
| can read by viewing the included agpl.txt or online |
|
||||
| at www.gnu.org/licenses/agpl.html. Removal of this |
|
||||
| copyright header is strictly prohibited without |
|
||||
| written permission from the original author(s). |
|
||||
+-------------------------------------------------------------*/
|
||||
|
||||
use CRM_Twingle_ExtensionUtil as E;
|
||||
|
||||
/**
|
||||
* Profiles define how incoming submissions from the Twingle API are
|
||||
* processed in CiviCRM.
|
||||
*/
|
||||
class CRM_Twingle_Profile {
|
||||
|
||||
/**
|
||||
* @var CRM_Twingle_Profile[] $_profiles
|
||||
* Caches the profile objects.
|
||||
*/
|
||||
protected static $_profiles = NULL;
|
||||
|
||||
/**
|
||||
* @var string $name
|
||||
* The name of the profile.
|
||||
*/
|
||||
protected $name = NULL;
|
||||
|
||||
/**
|
||||
* @var array $data
|
||||
* The properties of the profile.
|
||||
*/
|
||||
protected $data = NULL;
|
||||
|
||||
/**
|
||||
* CRM_Twingle_Profile constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the profile.
|
||||
* @param array $data
|
||||
* The properties of the profile
|
||||
*/
|
||||
public function __construct($name, $data) {
|
||||
$this->name = $name;
|
||||
$allowed_attributes = self::allowedAttributes();
|
||||
$this->data = $data + array_combine(
|
||||
$allowed_attributes,
|
||||
array_fill(0, count($allowed_attributes), NULL)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the profile's selector matches the given project ID.
|
||||
*
|
||||
* @param string | int $project_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matches($project_id) {
|
||||
$selector = $this->getAttribute('selector');
|
||||
$project_ids = explode(',', $selector);
|
||||
return in_array($project_id, $project_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all data attributes of the profile.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the profile name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the profile name.
|
||||
*
|
||||
* @param $name
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an attribute of the profile.
|
||||
*
|
||||
* @param string $attribute_name
|
||||
*
|
||||
* @return mixed | NULL
|
||||
*/
|
||||
public function getAttribute($attribute_name) {
|
||||
if (isset($this->data[$attribute_name])) {
|
||||
return $this->data[$attribute_name];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute of the profile.
|
||||
*
|
||||
* @param string $attribute_name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws \Exception
|
||||
* When the attribute name is not known.
|
||||
*/
|
||||
public function setAttribute($attribute_name, $value) {
|
||||
if (!in_array($attribute_name, self::allowedAttributes())) {
|
||||
throw new Exception(E::ts('Unknown attribute %1.', array(1 => $attribute_name)));
|
||||
}
|
||||
// TODO: Check if value is acceptable.
|
||||
$this->data[$attribute_name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies whether the profile is valid (i.e. consistent and not colliding
|
||||
* with other profiles).
|
||||
*
|
||||
* @throws Exception
|
||||
* When the profile could not be successfully validated.
|
||||
*/
|
||||
public function verifyProfile() {
|
||||
// TODO: check
|
||||
// data of this profile consistent?
|
||||
// conflicts with other profiles?
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the profile within the CiviCRM settings.
|
||||
*/
|
||||
public function saveProfile() {
|
||||
self::$_profiles[$this->getName()] = $this;
|
||||
$this->verifyProfile();
|
||||
self::storeProfiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the profile from the CiviCRM settings.
|
||||
*/
|
||||
public function deleteProfile() {
|
||||
unset(self::$_profiles[$this->getName()]);
|
||||
self::storeProfiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of attributes allowed for a profile.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function allowedAttributes() {
|
||||
// TODO: Adjust attributes for Twingle.
|
||||
return array(
|
||||
'selector',
|
||||
'location_type_id',
|
||||
'financial_type_id',
|
||||
'campaign_id',
|
||||
'pi_creditcard',
|
||||
'pi_sepa',
|
||||
'pi_paypal',
|
||||
'groups',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default profile with "factory" defaults.
|
||||
*
|
||||
* @param string $name
|
||||
* The profile name. Defaults to "default".
|
||||
*
|
||||
* @return CRM_Twingle_Profile
|
||||
*/
|
||||
public static function createDefaultProfile($name = 'default') {
|
||||
// TODO: Adjust attributes for Twingle.
|
||||
return new CRM_Twingle_Profile($name, array(
|
||||
'selector' => '',
|
||||
'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK,
|
||||
'financial_type_id' => 1, // "Donation"
|
||||
'campaign_id' => '',
|
||||
'pi_creditcard' => 1, // "Credit Card"
|
||||
'pi_sepa' => 5, // "EFT"
|
||||
'pi_paypal' => 3, // "Debit"
|
||||
'groups' => '',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the profile that matches the given project ID, i.e. the profile
|
||||
* which is responsible for processing the project's data.
|
||||
* Returns the default profile if no match was found.
|
||||
*
|
||||
* @param $project_id
|
||||
*
|
||||
* @return CRM_Twingle_Profile
|
||||
*/
|
||||
public static function getProfileForProject($project_id) {
|
||||
$profiles = self::getProfiles();
|
||||
foreach ($profiles as $profile) {
|
||||
if ($profile->matches($project_id)) {
|
||||
return $profile;
|
||||
}
|
||||
}
|
||||
|
||||
// No profile matched, return default profile.
|
||||
return $profiles['default'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the profil with the given name.
|
||||
*
|
||||
* @param $name
|
||||
*
|
||||
* @return CRM_Twingle_Profile | NULL
|
||||
*/
|
||||
public static function getProfile($name) {
|
||||
$profiles = self::getProfiles();
|
||||
if (isset($profiles[$name])) {
|
||||
return $profiles[$name];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of all profiles persisted within the current CiviCRM
|
||||
* settings, including the default profile.
|
||||
*
|
||||
* @return CRM_Twingle_Profile[]
|
||||
*/
|
||||
public static function getProfiles() {
|
||||
if (self::$_profiles === NULL) {
|
||||
self::$_profiles = array();
|
||||
if ($profiles_data = CRM_Core_BAO_Setting::getItem('de.systopia.twingle', 'twingle_profiles')) {
|
||||
foreach ($profiles_data as $profile_name => $profile_data) {
|
||||
self::$_profiles[$profile_name] = new CRM_Twingle_Profile($profile_name, $profile_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include the default profile if it was not overridden within the settings.
|
||||
if (!isset(self::$_profiles['default'])) {
|
||||
self::$_profiles['default'] = self::createDefaultProfile();
|
||||
self::storeProfiles();
|
||||
}
|
||||
|
||||
return self::$_profiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Persists the list of profiles into the CiviCRM settings.
|
||||
*/
|
||||
public static function storeProfiles() {
|
||||
$profile_data = array();
|
||||
foreach (self::$_profiles as $profile_name => $profile) {
|
||||
$profile_data[$profile_name] = $profile->data;
|
||||
}
|
||||
CRM_Core_BAO_Setting::setItem((object) $profile_data, 'de.systopia.twingle', 'twingle_profiles');
|
||||
}
|
||||
}
|
237
CRM/Twingle/Submission.php
Normal file
237
CRM/Twingle/Submission.php
Normal file
|
@ -0,0 +1,237 @@
|
|||
<?php
|
||||
/*------------------------------------------------------------+
|
||||
| SYSTOPIA Twingle Integration |
|
||||
| Copyright (C) 2018 SYSTOPIA |
|
||||
| Author: J. Schuppe (schuppe@systopia.de) |
|
||||
+-------------------------------------------------------------+
|
||||
| This program is released as free software under the |
|
||||
| Affero GPL license. You can redistribute it and/or |
|
||||
| modify it under the terms of this license which you |
|
||||
| can read by viewing the included agpl.txt or online |
|
||||
| at www.gnu.org/licenses/agpl.html. Removal of this |
|
||||
| copyright header is strictly prohibited without |
|
||||
| written permission from the original author(s). |
|
||||
+-------------------------------------------------------------*/
|
||||
|
||||
use CRM_Twingle_ExtensionUtil as E;
|
||||
|
||||
class CRM_Twingle_Submission {
|
||||
|
||||
/**
|
||||
* The default ID of the "Work" location type.
|
||||
*/
|
||||
const LOCATION_TYPE_ID_WORK = 2;
|
||||
|
||||
/**
|
||||
* The default ID of the "Employer of" relationship type.
|
||||
*/
|
||||
const EMPLOYER_RELATIONSHIP_TYPE_ID = 5;
|
||||
|
||||
/**
|
||||
* @param array &$params
|
||||
* A reference to the parameters array of the submission.
|
||||
*
|
||||
* @param \CRM_Twingle_Profile $profile
|
||||
* The Twingle profile to use for validation, defaults to the default
|
||||
* profile.
|
||||
*
|
||||
* @throws \CiviCRM_API3_Exception
|
||||
* When invalid parameters have been submitted.
|
||||
*/
|
||||
public static function validateSubmission(&$params, $profile = NULL) {
|
||||
if (!$profile) {
|
||||
$profile = CRM_Twingle_Profile::createDefaultProfile();
|
||||
}
|
||||
|
||||
// Validate donation rhythm.
|
||||
if (!in_array($params['donation_rhythm'], array(
|
||||
'one_time',
|
||||
'halfyearly',
|
||||
'quarterly',
|
||||
'yearly',
|
||||
'monthly',
|
||||
))) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Invalid donation rhythm.'),
|
||||
'invalid_format'
|
||||
);
|
||||
}
|
||||
|
||||
// Get the payment instrument defined within the profile, or return an error
|
||||
// if none matches (i.e. an unknown payment method was submitted).
|
||||
if (!$payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method'])) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Payment method could not be matched to existing payment instrument.'),
|
||||
'invalid_format'
|
||||
);
|
||||
}
|
||||
$params['payment_instrument_id'] = $payment_instrument_id;
|
||||
|
||||
// Validate date for parameter "confirmed_at".
|
||||
if (!DateTime::createFromFormat('Ymd', $params['confirmed_at'])) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Invalid date for parameter "confirmed_at".'),
|
||||
'invalid_format'
|
||||
);
|
||||
}
|
||||
|
||||
// Validate date for parameter "user_birthdate".
|
||||
if (!empty($params['user_birthdate']) && !DateTime::createFromFormat('Ymd', $params['user_birthdate'])) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Invalid date for parameter "user_birthdate".'),
|
||||
'invalid_format'
|
||||
);
|
||||
}
|
||||
|
||||
// Get the gender ID defined within the profile, or return an error if none
|
||||
// matches (i.e. an unknown gender was submitted).
|
||||
if (!$gender_id = $profile->getAttribute('gender_' . $params['user_gender'])) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Gender could not be matched to existing gender.'),
|
||||
'invalid_format'
|
||||
);
|
||||
}
|
||||
$params['gender_id'] = $gender_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the contact matching the given contact data or creates a new
|
||||
* contact.
|
||||
*
|
||||
* @param string $contact_type
|
||||
* The contact type to look for/to create.
|
||||
* @param array $contact_data
|
||||
* Data to use for contact lookup/to create a contact with.
|
||||
*
|
||||
* @return int | NULL
|
||||
* The ID of the matching/created contact, or NULL if no matching contact
|
||||
* was found and no new contact could be created.
|
||||
* @throws \CiviCRM_API3_Exception
|
||||
* When invalid data was given.
|
||||
*/
|
||||
public static function getContact($contact_type, $contact_data) {
|
||||
// If no parameters are given, do nothing.
|
||||
if (empty($contact_data)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Prepare values: country.
|
||||
if (!empty($contact_data['country'])) {
|
||||
if (is_numeric($contact_data['country'])) {
|
||||
// If a country ID is given, update the parameters.
|
||||
$contact_data['country_id'] = $contact_data['country'];
|
||||
unset($contact_data['country']);
|
||||
}
|
||||
else {
|
||||
// Look up the country depending on the given ISO code.
|
||||
$country = civicrm_api3('Country', 'get', array('iso_code' => $contact_data['country']));
|
||||
if (!empty($country['id'])) {
|
||||
$contact_data['country_id'] = $country['id'];
|
||||
unset($contact_data['country']);
|
||||
}
|
||||
else {
|
||||
throw new \CiviCRM_API3_Exception(
|
||||
E::ts('Unknown country %1.', array(1 => $contact_data['country'])),
|
||||
'invalid_format'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass to XCM.
|
||||
$contact_data['contact_type'] = $contact_type;
|
||||
$contact = civicrm_api3('Contact', 'getorcreate', $contact_data);
|
||||
if (empty($contact['id'])) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $contact['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shares an organisation's work address, unless the contact already has one.
|
||||
*
|
||||
* @param $contact_id
|
||||
* The ID of the contact to share the organisation address with.
|
||||
* @param $organisation_id
|
||||
* The ID of the organisation whose address to share with the contact.
|
||||
* @param $location_type_id
|
||||
* The ID of the location type to use for address lookup.
|
||||
*
|
||||
* @return boolean
|
||||
* Whether the organisation address has been shared with the contact.
|
||||
*
|
||||
* @throws \CiviCRM_API3_Exception
|
||||
* When looking up or creating the shared address failed.
|
||||
*/
|
||||
public static function shareWorkAddress($contact_id, $organisation_id, $location_type_id = self::LOCATION_TYPE_ID_WORK) {
|
||||
if (empty($organisation_id)) {
|
||||
// Only if organisation exists.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check whether organisation has a WORK address.
|
||||
$existing_org_addresses = civicrm_api3('Address', 'get', array(
|
||||
'contact_id' => $organisation_id,
|
||||
'location_type_id' => $location_type_id));
|
||||
if ($existing_org_addresses['count'] <= 0) {
|
||||
// Organisation does not have a WORK address.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check whether contact already has a WORK address.
|
||||
$existing_contact_addresses = civicrm_api3('Address', 'get', array(
|
||||
'contact_id' => $contact_id,
|
||||
'location_type_id' => $location_type_id));
|
||||
if ($existing_contact_addresses['count'] > 0) {
|
||||
// Contact already has a WORK address.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create a shared address.
|
||||
$address = reset($existing_org_addresses['values']);
|
||||
$address['contact_id'] = $contact_id;
|
||||
$address['master_id'] = $address['id'];
|
||||
unset($address['id']);
|
||||
civicrm_api3('Address', 'create', $address);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates or creates an employer relationship between contact and
|
||||
* organisation.
|
||||
*
|
||||
* @param int $contact_id
|
||||
* The ID of the employee contact.
|
||||
* @param int $organisation_id
|
||||
* The ID of the employer contact.
|
||||
*
|
||||
* @throws \CiviCRM_API3_Exception
|
||||
*/
|
||||
public static function updateEmployerRelation($contact_id, $organisation_id) {
|
||||
if (empty($contact_id) || empty($organisation_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// see if there is already one
|
||||
$existing_relationship = civicrm_api3('Relationship', 'get', array(
|
||||
'relationship_type_id' => self::EMPLOYER_RELATIONSHIP_TYPE_ID,
|
||||
'contact_id_a' => $contact_id,
|
||||
'contact_id_b' => $organisation_id,
|
||||
'is_active' => 1,
|
||||
));
|
||||
|
||||
if ($existing_relationship['count'] == 0) {
|
||||
// There is currently no (active) relationship between these contacts.
|
||||
$new_relationship_data = array(
|
||||
'relationship_type_id' => self::EMPLOYER_RELATIONSHIP_TYPE_ID,
|
||||
'contact_id_a' => $contact_id,
|
||||
'contact_id_b' => $organisation_id,
|
||||
'is_active' => 1,
|
||||
);
|
||||
|
||||
civicrm_api3('Relationship', 'create', $new_relationship_data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
460
api/v3/TwingleDonation/Submit.php
Normal file
460
api/v3/TwingleDonation/Submit.php
Normal file
|
@ -0,0 +1,460 @@
|
|||
<?php
|
||||
/*------------------------------------------------------------+
|
||||
| SYSTOPIA Twingle Integration |
|
||||
| Copyright (C) 2018 SYSTOPIA |
|
||||
| Author: J. Schuppe (schuppe@systopia.de) |
|
||||
+-------------------------------------------------------------+
|
||||
| This program is released as free software under the |
|
||||
| Affero GPL license. You can redistribute it and/or |
|
||||
| modify it under the terms of this license which you |
|
||||
| can read by viewing the included agpl.txt or online |
|
||||
| at www.gnu.org/licenses/agpl.html. Removal of this |
|
||||
| copyright header is strictly prohibited without |
|
||||
| written permission from the original author(s). |
|
||||
+-------------------------------------------------------------*/
|
||||
|
||||
use CRM_Twingle_ExtensionUtil as E;
|
||||
|
||||
/**
|
||||
* TwingleDonation.Submit API specification
|
||||
* This is used for documentation and validation.
|
||||
*
|
||||
* @param array $params
|
||||
* Description of fields supported by this API call.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @see http://wiki.civicrm.org/confluence/display/CRMDOC/API+Architecture+Standards
|
||||
*/
|
||||
function _civicrm_api3_twingle_donation_Submit_spec(&$params) {
|
||||
$params['project_id'] = array(
|
||||
'name' => 'project_id',
|
||||
'title' => 'Project ID',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 1,
|
||||
'description' => 'The Twingle project ID.',
|
||||
);
|
||||
$params['trx_id'] = array(
|
||||
'name' => 'trx_id',
|
||||
'title' => 'Transaction ID',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 1,
|
||||
'description' => 'The unique transaction ID of the donation',
|
||||
);
|
||||
$params['confirmed_at'] = array(
|
||||
'name' => 'confirmed_at',
|
||||
'title' => 'Confirmed at',
|
||||
'type' => CRM_Utils_Type::T_INT,
|
||||
'api.required' => 1,
|
||||
'description' => 'The date when the donation was issued, format: YYYYMMDD.',
|
||||
);
|
||||
$params['purpose'] = array(
|
||||
'name' => 'purpose',
|
||||
'title' => 'Purpose',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The purpose of the donation.',
|
||||
);
|
||||
$params['amount'] = array(
|
||||
'name' => 'amount',
|
||||
'title' => 'Amount',
|
||||
'type' => CRM_Utils_Type::T_INT,
|
||||
'api.required' => 1,
|
||||
'description' => 'The donation amount in minor currency unit.',
|
||||
);
|
||||
$params['currency'] = array(
|
||||
'name' => 'currency',
|
||||
'title' => 'Currency',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 1,
|
||||
'description' => 'The ISO-4217 currency code of the donation.',
|
||||
);
|
||||
$params['newsletter'] = array(
|
||||
'name' => 'newsletter',
|
||||
'title' => 'Newsletter',
|
||||
'type' => CRM_Utils_Type::T_BOOLEAN,
|
||||
'api.required' => 0,
|
||||
'description' => 'Whether to subscribe the contact to the newsletter group defined in the profile.',
|
||||
);
|
||||
$params['postinfo'] = array(
|
||||
'name' => 'postinfo',
|
||||
'title' => 'Postal mailing',
|
||||
'type' => CRM_Utils_Type::T_BOOLEAN,
|
||||
'api.required' => 0,
|
||||
'description' => 'Whether to subscribe the contact to the postal mailing group defined in the profile.',
|
||||
);
|
||||
$params['donation_receipt'] = array(
|
||||
'name' => 'donation_receipt',
|
||||
'title' => 'Donation receipt',
|
||||
'type' => CRM_Utils_Type::T_BOOLEAN,
|
||||
'api.required' => 0,
|
||||
'description' => 'Whether the contact requested a donation receipt.',
|
||||
);
|
||||
$params['payment_method'] = array(
|
||||
'name' => 'payment_method',
|
||||
'title' => 'Payment method',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 1,
|
||||
'description' => 'The Twingle payment method used for the donation.',
|
||||
);
|
||||
$params['donation_rhythm'] = array(
|
||||
'name' => 'donation_rhythm',
|
||||
'title' => 'Donation rhythm',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 1,
|
||||
'description' => 'The interval which the donation is recurring in.',
|
||||
);
|
||||
$params['debit_iban'] = array(
|
||||
'name' => 'debit_iban',
|
||||
'title' => 'SEPA IBAN',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The IBAN for SEPA Direct Debit payments, conforming with ISO 13616-1:2007.',
|
||||
);
|
||||
$params['debit_bic'] = array(
|
||||
'name' => 'debit_bic',
|
||||
'title' => 'SEPA BIC',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The BIC for SEPA Direct Debit payments, conforming with ISO 9362.',
|
||||
);
|
||||
$params['debit_mandate_reference'] = array(
|
||||
'name' => 'debit_mandate_reference',
|
||||
'title' => 'SEPA Direct Debit Mandate reference',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The mandate for SEPA Direct Debit payments.',
|
||||
);
|
||||
$params['debit_account_holder'] = array(
|
||||
'name' => 'debit_account_holder',
|
||||
'title' => 'SEPA Direct Debit Account holder',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The mandate for SEPA Direct Debit payments.',
|
||||
);
|
||||
$params['is_anonymous'] = array(
|
||||
'name' => 'is_anonymous',
|
||||
'title' => 'Anonymous donation',
|
||||
'type' => CRM_Utils_Type::T_BOOLEAN,
|
||||
'api.required' => 0,
|
||||
'api.default' => 0,
|
||||
'description' => 'Whether the donation is submitted anonymously.',
|
||||
);
|
||||
$params['user_gender'] = array(
|
||||
'name' => 'user_gender',
|
||||
'title' => 'Gender',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The gender of the contact.',
|
||||
);
|
||||
$params['user_birthdate'] = array(
|
||||
'name' => 'user_birthdate',
|
||||
'title' => 'Date of birth',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The date of birth of the contact, format: YYYYMMDD.',
|
||||
);
|
||||
$params['user_title'] = array(
|
||||
'name' => 'user_title',
|
||||
'title' => 'Formal title',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The formal title of the contact.',
|
||||
);
|
||||
$params['user_email'] = array(
|
||||
'name' => 'user_email',
|
||||
'title' => 'E-mail address',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The e-mail address of the contact.',
|
||||
);
|
||||
$params['user_firstname'] = array(
|
||||
'name' => 'user_firstname',
|
||||
'title' => 'First name',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The first name of the contact.',
|
||||
);
|
||||
$params['user_lastname'] = array(
|
||||
'name' => 'user_lastname',
|
||||
'title' => 'Last name',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The last name of the contact.',
|
||||
);
|
||||
$params['user_street'] = array(
|
||||
'name' => 'user_street',
|
||||
'title' => 'Street address',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The street address of the contact.',
|
||||
);
|
||||
$params['user_postal_code'] = array(
|
||||
'name' => 'user_postal_code',
|
||||
'title' => 'Postal code',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The postal code of the contact.',
|
||||
);
|
||||
$params['user_city'] = array(
|
||||
'name' => 'user_city',
|
||||
'title' => 'City',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The city of the contact.',
|
||||
);
|
||||
$params['user_telephone'] = array(
|
||||
'name' => 'user_telephone',
|
||||
'title' => 'Telephone',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The telephone number of the contact.',
|
||||
);
|
||||
$params['user_company'] = array(
|
||||
'name' => 'user_company',
|
||||
'title' => 'Company',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'The company of the contact.',
|
||||
);
|
||||
$params['user_extrafield'] = array(
|
||||
'name' => 'user_extrafield',
|
||||
'title' => 'User extra field',
|
||||
'type' => CRM_Utils_Type::T_STRING,
|
||||
'api.required' => 0,
|
||||
'description' => 'Additional information of the contact.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* TwingleDonation.Submit API
|
||||
*
|
||||
* @param array $params
|
||||
* @return array API result descriptor
|
||||
* @see civicrm_api3_create_success
|
||||
* @see civicrm_api3_create_error
|
||||
*/
|
||||
function civicrm_api3_twingle_donation_Submit($params) {
|
||||
try {
|
||||
// Copy submitted parameters.
|
||||
$original_params = $params;
|
||||
|
||||
// Get the profile defined for the given form ID, or the default profile
|
||||
// if none matches.
|
||||
$profile = CRM_Twingle_Profile::getProfileForProject($params['project_id']);
|
||||
|
||||
// Validate submitted parameters
|
||||
CRM_Twingle_Submission::validateSubmission($params, $profile);
|
||||
|
||||
// Do not process an already existing contribution with the given
|
||||
// transaction ID.
|
||||
$contribution = civicrm_api3('Contribution', 'get', array(
|
||||
'trxn_id' => $params['trx_id']
|
||||
));
|
||||
if ($contribution['count'] > 0) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Contribution with the given transaction ID already exists.'),
|
||||
'api_error'
|
||||
);
|
||||
}
|
||||
|
||||
// Create contact(s).
|
||||
if ($params['is_anonymous']) {
|
||||
// Retrieve the ID of the contact to use for anonymous donations defined
|
||||
// within the profile
|
||||
$contact_id = civicrm_api3('Contact', 'getsingle', array(
|
||||
'id' => $profile->getAttribute('anonymous_contact_id'),
|
||||
))['id'];
|
||||
}
|
||||
else {
|
||||
// Prepare parameter mapping for address.
|
||||
foreach (array(
|
||||
'user_street' => 'street_address',
|
||||
'user_postal_code' => 'postal_code',
|
||||
'user_city' => 'city',
|
||||
) as $address_param => $address_component) {
|
||||
if (!empty($params[$address_param])) {
|
||||
$params[$address_component] = $params[$address_param];
|
||||
if ($address_param != $address_component) {
|
||||
unset($params[$address_param]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare parameter mapping for organisation.
|
||||
if (!empty($params['user_company'])) {
|
||||
$params['organization_name'] = $params['user_company'];
|
||||
unset($params['user_company']);
|
||||
}
|
||||
|
||||
// Remove parameter "id".
|
||||
if (!empty($params['id'])) {
|
||||
unset($params['id']);
|
||||
}
|
||||
|
||||
// Add location type to parameters.
|
||||
$params['location_type_id'] = (int) $profile->getAttribute('location_type_id');
|
||||
|
||||
// Exclude address for now when retrieving/creating the individual contact
|
||||
// and an organisation is given, as we are checking organisation address
|
||||
// first and share it with the individual.
|
||||
if (!empty($params['organization_name'])) {
|
||||
$submitted_address = array();
|
||||
foreach (array(
|
||||
'street_address',
|
||||
'postal_code',
|
||||
'city',
|
||||
'country',
|
||||
'location_type_id',
|
||||
) as $address_component) {
|
||||
if (!empty($params[$address_component])) {
|
||||
$submitted_address[$address_component] = $params[$address_component];
|
||||
unset($params[$address_component]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the ID of the contact matching the given contact data, or create a
|
||||
// new contact if none exists for the given contact data.
|
||||
$contact_data = array();
|
||||
foreach (array(
|
||||
'user_firstname' => 'first_name',
|
||||
'user_lastname' => 'last_name',
|
||||
'gender_id' => 'gender_id',
|
||||
'user_birthdate' => 'birth_date',
|
||||
'user_email' => 'email',
|
||||
'user_telephone' => 'phone',
|
||||
) as $contact_param => $contact_component) {
|
||||
if (!empty($params[$contact_param])) {
|
||||
$contact_data[$contact_component] = $params[$contact_param];
|
||||
}
|
||||
}
|
||||
if (!$contact_id = CRM_Twingle_Submission::getContact(
|
||||
'Individual',
|
||||
$contact_data
|
||||
)) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Individual contact could not be found or created.'),
|
||||
'api_error'
|
||||
);
|
||||
}
|
||||
|
||||
// Organisation lookup.
|
||||
if (!empty($params['organization_name'])) {
|
||||
$organisation_data = array(
|
||||
'organization_name' => $params['organization_name'],
|
||||
);
|
||||
if (!empty($submitted_address)) {
|
||||
$params += $submitted_address;
|
||||
}
|
||||
if (!$organisation_id = CRM_Twingle_Submission::getContact(
|
||||
'Organization',
|
||||
$organisation_data
|
||||
)) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Organisation contact could not be found or created.'),
|
||||
'api_error'
|
||||
);
|
||||
}
|
||||
}
|
||||
$address_shared = (
|
||||
isset($organisation_id)
|
||||
&& CRM_Twingle_Submission::shareWorkAddress(
|
||||
$contact_id,
|
||||
$organisation_id,
|
||||
$params['location_type_id']
|
||||
)
|
||||
);
|
||||
|
||||
// Address is not shared, use submitted address.
|
||||
if (!$address_shared && !empty($submitted_address)) {
|
||||
$submitted_address['contact_id'] = $contact_id;
|
||||
civicrm_api3('Address', 'create', $submitted_address);
|
||||
}
|
||||
|
||||
// Create employer relationship between organization and individual.
|
||||
if (isset($organisation_id)) {
|
||||
CRM_Twingle_Submission::updateEmployerRelation($contact_id, $organisation_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Create contribution or SEPA mandate.
|
||||
$contribution_data = array(
|
||||
'contact_id' => (isset($organisation_id) ? $organisation_id : $contact_id),
|
||||
'currency' => $params['currency'],
|
||||
'trxn_id' => $params['trx_id'],
|
||||
'financial_type_id' => $profile->getAttribute('financial_type_id'),
|
||||
'payment_instrument_id' => $params['payment_instrument_id'],
|
||||
'amount' => $params['amount'],
|
||||
'total_amount' => $params['amount'],
|
||||
);
|
||||
if (!empty($params['purpose'])) {
|
||||
$contribution_data['note'] = $params['purpose'];
|
||||
}
|
||||
|
||||
$sepa_extension = civicrm_api3('Extension', 'get', array(
|
||||
'full_name' => 'org.project60.sepa',
|
||||
'is_active' => 1,
|
||||
));
|
||||
if ($sepa_extension['count'] && CRM_Sepa_Logic_Settings::isSDD($contribution_data)) {
|
||||
// If CiviSEPA is installed and the financial type is a CiviSEPA-one,
|
||||
// create SEPA mandate (and recurring contribution, using "createfull" API
|
||||
// action).
|
||||
$mandate_data = $contribution_data + array(
|
||||
'type' => ($params['donation_rhythm'] == 'one_time' ? 'OOFF' : 'RCUR'),
|
||||
'iban' => $params['debit_iban'],
|
||||
'bic' => $params['debit_bic'],
|
||||
'reference' => $params['debit_mandate_reference'],
|
||||
'date' => $params['confirmed_at'],
|
||||
'creditor_id' => $profile->getAttribute('sepa_creditor_id'),
|
||||
);
|
||||
$mandate = civicrm_api3('SepaMandate', 'createfull', $mandate_data);
|
||||
if ($mandate['is_error']) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Could not create SEPA mandate'),
|
||||
'api_error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create (recurring) contribution.
|
||||
if ($params['donation_rhythm'] != 'one_time') {
|
||||
// Create recurring contribution first.
|
||||
$contribution_recur_data = $contribution_data + array(
|
||||
'frequency_interval' => '', // TODO
|
||||
'contribution_status_id' => 'Pending', // TODO: Or "In Progress"?
|
||||
'start_date' => $params['confirmed_at'],
|
||||
);
|
||||
$contribution_recur = civicrm_api3('contributionRecur', 'create', $contribution_recur_data);
|
||||
if ($contribution_recur['is_error']) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Could not create recurring contribution.'),
|
||||
'api_error'
|
||||
);
|
||||
}
|
||||
$contribution_data['contribution_recur_id'] = $contribution_recur['id'];
|
||||
}
|
||||
|
||||
// Create contribution.
|
||||
$contribution_data += array(
|
||||
'contribution_status_id' => 'Completed',
|
||||
'receive_date' => $params['confirmed_at'],
|
||||
);
|
||||
$contribution = civicrm_api3('Contribution', 'create', $contribution_data);
|
||||
if ($contribution['is_error']) {
|
||||
throw new CiviCRM_API3_Exception(
|
||||
E::ts('Could not create contribution'),
|
||||
'api_error'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Assemble return data.
|
||||
$result = civicrm_api3_create_success();
|
||||
}
|
||||
catch (CiviCRM_API3_Exception $exception) {
|
||||
$result = civicrm_api3_create_error($exception->getMessage());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
3
info.xml
3
info.xml
|
@ -21,6 +21,9 @@
|
|||
<ver>4.7</ver>
|
||||
</compatibility>
|
||||
<comments></comments>
|
||||
<requires>
|
||||
<ext>de.systopia.xcm</ext>
|
||||
</requires>
|
||||
<civix>
|
||||
<namespace>CRM/Twingle</namespace>
|
||||
</civix>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue