Skip to content
This repository was archived by the owner on Feb 11, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

name: Run tests

env:
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}

# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches:
- master
- stripe-endpoint
branches: [ master ]
pull_request:
branches: [ master ]

Expand All @@ -21,6 +22,7 @@ jobs:
build:
# The type of runner that the job will run on
runs-on: ubuntu-18.04
environment: testing

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
Expand Down Expand Up @@ -62,7 +64,12 @@ jobs:
with:
path: drupal/sites/all/modules/civicrm/ext/we-act

- name: Run composer
run: composer install
working-directory: drupal/sites/all/modules/civicrm/ext/we-act

- name: Run unit tests
run: phpunit
run: STRIPE_SECRET_KEY=${{ secrets.STRIPE_SECRET_KEY }} phpunit
working-directory: drupal/sites/all/modules/civicrm/ext/we-act


3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ Session.vim
*~
# auto-generated tag files
tags

vendor
composer.lock
27 changes: 9 additions & 18 deletions CRM/WeAct/Action/Donation.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function createContribRecur($campaign_id, $contact_id, $action_page, $loc
if (substr($this->processor, -5) == '-sepa') {
$create_mandate = $this->createMandate($contact_id, 'RCUR', $campaign_id, $action_page);
//Mandates don't have utm fields, so associate them to recurring contrib created along with mandate
civicrm_api3('ContributionRecur', 'create', [
return civicrm_api3('ContributionRecur', 'create', [
'id' => $create_mandate['values'][0]['entity_id'],
$this->settings->customFields['recur_utm_source'] => CRM_Utils_Array::value('source', $utm),
$this->settings->customFields['recur_utm_medium'] => CRM_Utils_Array::value('medium', $utm),
Expand All @@ -57,19 +57,6 @@ public function createContribRecur($campaign_id, $contact_id, $action_page, $loc
} else {
$processor_id = $this->settings->paymentProcessorIds[$this->processor];

if ($this->processor == 'proca-stripe') {
//Stripe webhook requires a link customer<->contact to process events, so we create it here if needed
$customer_params = [
'customer_id' => $this->providerDonorId,
'contact_id' => $contact_id,
'processor_id' => $processor_id
];
$customer = civicrm_api3('StripeCustomer', 'get', $customer_params);
if ($customer['count'] == 0) {
civicrm_api3('StripeCustomer', 'create', $customer_params);
}
}

$params = [
'sequential' => 1,
'contact_id' => $contact_id,
Expand All @@ -91,7 +78,7 @@ public function createContribRecur($campaign_id, $contact_id, $action_page, $loc
$this->settings->customFields['recur_utm_campaign'] => CRM_Utils_Array::value('campaign', $utm),
];
$create_recur = civicrm_api3('ContributionRecur', 'create', $params);
$this->createContrib($campaign_id, $contact_id, $action_page, $location, $utm, $create_recur['id']);
return $this->createContrib($campaign_id, $contact_id, $action_page, $location, $utm, $create_recur['id']);
}
}

Expand All @@ -100,7 +87,7 @@ public function createContrib($campaign_id, $contact_id, $action_page, $location
if (substr($this->processor, -5) == '-sepa') {
$create_mandate = $this->createMandate($contact_id, 'OOFF', $campaign_id, $action_page);
//Mandates don't have utm fields, so associate them to recurring contrib created along with mandate
civicrm_api3('Contribution', 'create', [
$created = civicrm_api3('Contribution', 'create', [
'id' => $create_mandate['values'][0]['entity_id'],
$this->settings->customFields['utm_source'] => CRM_Utils_Array::value('source', $utm),
$this->settings->customFields['utm_medium'] => CRM_Utils_Array::value('medium', $utm),
Expand Down Expand Up @@ -130,15 +117,19 @@ public function createContrib($campaign_id, $contact_id, $action_page, $location
];
if ($recurring_id) {
$params['contribution_recur_id'] = $recurring_id;
//The utm params will be set by a hook in contributm extension, let's not mess with it
//The utm params will be set by a hook in contributm extension, let's
//not mess with it
$created = NULL;
}
else {
$params[$this->settings->customFields['utm_source']] = CRM_Utils_Array::value('source', $utm);
$params[$this->settings->customFields['utm_medium']] = CRM_Utils_Array::value('medium', $utm);
$params[$this->settings->customFields['utm_campaign']] = CRM_Utils_Array::value('campaign', $utm);
}
civicrm_api3('Contribution', 'create', $params);
$created = civicrm_api3('Contribution', 'create', $params);
}

return $created;
}

protected function createMandate($contact_id, $mandate_type, $campaign_id, $source) {
Expand Down
45 changes: 32 additions & 13 deletions CRM/WeAct/Action/Proca.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ public function __construct($json_msg) {
$this->createdAt = $json_msg->action->createdAt;
$this->actionPageId = $json_msg->actionPageId;
$this->actionPageName = $json_msg->actionPage->name;
$this->language = $this->determineLanguage($json_msg->actionPage->locale);
$this->contact = $this->buildContact(json_decode($json_msg->contact->payload));
$this->language = $this->contact->determineLanguage($json_msg->actionPage->locale);

$this->details = $this->buildDonation($json_msg->actionId, $json_msg->action);

$this->locationId = @$json_msg->action->fields->speakoutCampaign;
Expand All @@ -36,8 +37,26 @@ protected function buildContact($json_contact) {
return $contact;
}

protected function _lookupCharge($pi) {
$sk = CRM_Core_DAO::singleValueQuery(
"SELECT password FROM civicrm_payment_processor WHERE id = 1" // I know, but it works
);
if (!$sk) {
$sk = getenv("STRIPE_SECRET_KEY");
}
if (!$sk) {
throw new Exception("Oops, couldn't find a secret key for Stripe. Can't go on!");
}
$stripe = new \Stripe\StripeClient($sk);
$charges = $stripe->charges->all(['payment_intent' => $pi->id]);
if (! $charges->data) {
throw new Exception("Couldn't find a Charge for PaymentIntent: {$pi->id}");
}
return $charges->data[0];
}

protected function buildDonation($action_id, $json_action) {
$statusMap = ['succeeded' => 'Completed', 'failed' => 'Failed'];
// $statusMap = ['succeeded' => 'Completed', 'failed' => 'Failed'];
$frequencyMap = ['one_off' => 'one-off', 'monthly' => 'month', 'weekly' => 'week', 'daily' => 'day'];

$donation = new CRM_WeAct_Action_Donation();
Expand All @@ -60,12 +79,19 @@ protected function buildDonation($action_id, $json_action) {
} else if ($provider == 'stripe') {
$donation->paymentMethod = $json_action->donation->payload->paymentConfirm->payment_method_types[0];
$donation->isTest = !$json_action->donation->payload->paymentIntent->response->livemode;
if ($_ENV['CIVICRM_UF'] == 'UnitTests') {
$charge_id = property_exists($json_action->donation->payload, 'testingChargeId')
? $json_action->donation->payload->testingChargeId
: 'ch_yetanothercharge';
}
else {
$charge_id = $this->_lookupCharge($json_action->donation->payload->paymentIntent->response);
}
# this becomes civicrm_contribution.trxn_id
$donation->paymentId = $charge_id;
if ($donation->frequency == 'one-off') {
$donation->paymentId = $json_action->donation->payload->paymentIntent->response->id;
$donation->donationId = $donation->paymentId;
} else {
//Stripe webhook expects invoice id as trxn_id of contributions
$donation->paymentId = $json_action->donation->payload->paymentIntent->response->latest_invoice->id;
$donation->donationId = $json_action->donation->payload->subscriptionId;
$donation->providerDonorId = $json_action->donation->payload->customerId;
}
Expand All @@ -82,12 +108,5 @@ protected function buildDonation($action_id, $json_action) {
return $donation;
}

protected function determineLanguage($procaLanguage) {
$language = strtoupper($procaLanguage);
$countryLangMapping = Civi::settings()->get('country_lang_mapping');
if (array_key_exists($language, $countryLangMapping)) {
return $countryLangMapping[$language];
}
return 'en_GB';
}

}
68 changes: 28 additions & 40 deletions CRM/WeAct/ActionProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,40 @@ public function process(CRM_WeAct_Action $action) {
}

public function getOrCreateContact(CRM_WeAct_Action $action, $campaign_id) {
if ($action->contact->isAnonymous()) {
return $this->settings->anonymousId;
$result = $action->contact->createOrUpdate(
$action->language,
$action->source()
);

if ($this->requestConsents) {
$action->contact->sendConsents(
$result['id'],
$campaign_id,
[ 'source' => CRM_Utils_Array::value('source', $action->utm),
'medium' => CRM_Utils_Array::value('medium', $action->utm),
'campaign' => CRM_Utils_Array::value('campaign', $action->utm) ]
);
}

$contact_ids = $action->contact->getMatchingIds();
if (count($contact_ids) == 0) {
$contact = $action->contact->create($action->language, $action->source());
} else {
//There shouldn't be more than one contact, but if does we'll simply use the "oldest" one
//TODO send an alert to someone that a merge is required if more than one id
$contact_id = min($contact_ids);
$contact = $action->contact->getAndUpdate($contact_id);
}

Civi::log()->debug("Checking for group membership - {$contact['api.GroupContact.get']['count']}");

//Membership was retrieved from a joined query to GroupContact for the members group
if ($this->requestConsents && $contact['api.GroupContact.get']['count'] == 0) {
Civi::log()->debug("Sending consent request to contact {$contact['id']}");
$consentParams = [
'contact_id' => $contact['id'],
'campaign_id' => $campaign_id,
'utm_source' => CRM_Utils_Array::value('source', $action->utm),
'utm_medium' => CRM_Utils_Array::value('medium', $action->utm),
'utm_campaign' => CRM_Utils_Array::value('campaign', $action->utm),
];
civicrm_api3('Gidipirus', 'send_consent_request', $consentParams);
}

return $contact['id'];
return $result['id'];
}

public function processDonation($action, $campaign_id, $contact_id) {
CRM_Core_Transaction::create(TRUE)->run(function(CRM_Core_Transaction $tx) use ($action, $campaign_id, $contact_id) {
$donation = $action->details;
if ($donation->isRecurring()) {
$recur_id = $donation->findMatchingContribRecur();
if (!$recur_id) {
$donation->createContribRecur($campaign_id, $contact_id, $action->actionPageName, $action->location, $action->utm);
} else if (!$donation->findMatchingContrib()) {
$donation->createContrib($campaign_id, $contact_id, $action->actionPageName, $action->location, $action->utm, $recur_id);
CRM_Core_Transaction::create(TRUE)->run(
function(CRM_Core_Transaction $tx) use ($action, $campaign_id, $contact_id) {
$donation = $action->details;
if ($donation->isRecurring()) {
$recur_id = $donation->findMatchingContribRecur();
if (!$recur_id) {
return $donation->createContribRecur($campaign_id, $contact_id, $action->actionPageName, $action->location, $action->utm);
}
else if (!$donation->findMatchingContrib()) {
return $donation->createContrib($campaign_id, $contact_id, $action->actionPageName, $action->location, $action->utm, $recur_id);
}
}
else if (!$donation->findMatchingContrib()) {
return $donation->createContrib($campaign_id, $contact_id, $action->actionPageName, $action->location, $action->utm);
}
}
else if (!$donation->findMatchingContrib()) {
$donation->createContrib($campaign_id, $contact_id, $action->actionPageName, $action->location, $action->utm);
}
});
}
}
64 changes: 61 additions & 3 deletions CRM/WeAct/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class CRM_WeAct_Contact {
public $email;
public $postcode;
public $country;
public $isMember = false;

public function __construct() {
$this->settings = CRM_WeAct_Settings::instance();
Expand Down Expand Up @@ -34,6 +35,12 @@ public function getMatchingIds() {
}

public function create($language, $source) {

$country_code = NULL;
if (key_exists($this->country, $this->settings->countryIds)) {
$country_code = $this->settings->countryIds[$this->country];
}

$create_params = [
'sequential' => 1,
'contact_type' => 'Individual',
Expand All @@ -46,16 +53,67 @@ public function create($language, $source) {
'api.Address.create' => [
'location_type_id' => 1,
'postal_code' => $this->postcode,
'country_id' => $this->settings->countryIds[$this->country],
'country_id' => $country_code,
]
];
$create_result = civicrm_api3('Contact', 'create', $create_params);
$contact = $create_result['values'][0];
//Indicate to caller that the contact is not in the members group
$contact['api.GroupContact.get']['count'] = 0;

// Indicate to caller that the contact is not in the members group, because
// we just created them
// $contact['api.GroupContact.get']['count'] = 0;
$this->isMember = false;

return $contact;
}

public function determineLanguage($code) {
$language = strtoupper($code);
$countryLangMapping = Civi::settings()->get('country_lang_mapping');
if (array_key_exists($language, $countryLangMapping)) {
return $countryLangMapping[$language];
}
return 'en_GB';
}

public function createOrUpdate($language, $source) {
if ($this->isAnonymous()) {
return $this->settings->anonymousId;
}

$ids = $this->getMatchingIds();
if (count($ids) == 0) {
$contact = $this->create($language, $source);
} else {
//There shouldn't be more than one contact, but if does we'll simply use the "oldest" one
//TODO send an alert to someone that a merge is required if more than one id
$contact = $this->getAndUpdate(min($ids));
}

return $contact;
}


public function sendConsents($contact_id, $campaign_id, $utms = []) {

Civi::log()->debug("Checking for group membership - {$this->isMember}");

// Membership was retrieved from a joined query to GroupContact for the members group
if (! $this->isMember) {
Civi::log()->debug("Sending consent request to contact {$contact_id}");
$consentParams = [
'contact_id' => $contact_id,
'campaign_id' => $campaign_id,
'utm_source' => CRM_Utils_Array::value('source', $utms),
'utm_medium' => CRM_Utils_Array::value('medium', $utms),
'utm_campaign' => CRM_Utils_Array::value('campaign', $utms),
];
civicrm_api3('Gidipirus', 'send_consent_request', $consentParams);
}

}


public function getAndUpdate($contact_id) {
$get_params = [
'id' => $contact_id,
Expand Down
Loading