KoalectTransaction.php 15.2 KB
Newer Older
1
<?php
ErikHommel's avatar
ErikHommel committed
2
use CRM_Koalecttocivi_ExtensionUtil as E;
3
4

/**
5
 * Class for Koalect Transaction processing
6
7
8
9
 *
 * @author Erik Hommel (CiviCooP) <erik.hommel@civicoop.org>
 * @license AGPL-3.0
 */
10
class CRM_Koalecttocivi_KoalectTransaction {
11

12
  public function create(array $apiData) {
13
    $result = [];
ErikHommel's avatar
ErikHommel committed
14
    // store incoming data in log file
ErikHommel's avatar
ErikHommel committed
15
    $this->log($apiData);
ErikHommel's avatar
ErikHommel committed
16
    // actual processing is in the Form Processor koalect_transaction
ErikHommel's avatar
ErikHommel committed
17
18
    $data = $this->sanitizeIncomingParams($apiData);
    if ($data) {
19
      // temp debug during initial testing
20
      Civi::log()->debug('Koalect params that go into the Form Processor: ' . json_encode($data));
ErikHommel's avatar
ErikHommel committed
21
22
      try {
        $fp = civicrm_api3('FormProcessor', 'koalect_transaction', $data);
ErikHommel's avatar
ErikHommel committed
23
        Civi::log()->debug('Koalect Form Processor result:  ' . json_encode($fp));
ErikHommel's avatar
ErikHommel committed
24
        $result = $this->prepareResult($fp);
ErikHommel's avatar
ErikHommel committed
25
        // fix gender for contact if necessary, might not be done by the XCM Form Processor Action
26
        if ($result['contact_id'] && isset($result['gender_id'])) {
ErikHommel's avatar
ErikHommel committed
27
28
29
          $this->fixGender((int) $result['contact_id'], (int) $result['gender_id']);
          unset($result['gender_id']);
        }
ErikHommel's avatar
ErikHommel committed
30
31
32
33
        // fix phone number for contact if necessary, might not be done by the XCM Form Processor Action
        if ($apiData['customer']['phone_number'] && $result['contact_id']) {
          $this->fixPhone((int) $result['contact_id'], $apiData['customer']['phone_number']);
        }
ErikHommel's avatar
ErikHommel committed
34
35
      }
      catch (CiviCRM_API3_Exception $ex) {
36
        Civi::log()->debug('Koalect Form Processor error:  ' . $ex->getMessage());
ErikHommel's avatar
ErikHommel committed
37
38
39
40
41
42
        $result = [
          'is_error' => 1,
          'error_message' => $ex->getMessage()
          ];
      }
    }
43
    Civi::log()->debug('Result that will be returned:  ' . json_encode($result));
ErikHommel's avatar
ErikHommel committed
44
45
    return $result;
  }
ErikHommel's avatar
ErikHommel committed
46

ErikHommel's avatar
ErikHommel committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  /**
   * Method to add phone to contact if not found
   *
   * @param int $contactId
   * @param string $phone
   * @return void
   */
  private function fixPhone(int $contactId, string $phone) {
    if ($contactId && $phone) {
      $phoneNumeric = \Civi::service('koalecttocivi')->getPhoneNumeric($phone);
      // check if contact already has phone and add if not
      $query = "SELECT COUNT(*) FROM civicrm_phone WHERE contact_id = %1 AND phone_numeric = %2";
      $count = CRM_Core_DAO::singleValueQuery($query, [
        1 => [$contactId, "Integer"],
        2 => [(int) $phoneNumeric, "Integer"],
      ]);
      if ($count == 0) {
        if (function_exists('civicrm_api4')) {
          try {
            \Civi\Api4\Phone::create()
67
              ->setCheckPermissions(FALSE)
ErikHommel's avatar
ErikHommel committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
              ->addValue('contact_id', $contactId)
              ->addValue('location_type_id', \Civi::service('koalecttocivi')->getDefaultLocationTypeId())
              ->addValue('phone_type_id:name', 'Phone')
              ->addValue('phone', $phone)
              ->execute();
          }
          catch (API_Exception $ex) {
          }
        }
        else {
          try {
            civicrm_api3('Phone', 'create', [
              'contact_id' => $contactId,
              'location_type_id' => \Civi::service('koalecttocivi')->getDefaultLocationTypeId(),
              'phone_type_id' => 'Phone',
              'phone' => $phone,
            ]);
          }
          catch (CiviCRM_API3_Exception $ex) {
          }
        }

      }
    }
  }

ErikHommel's avatar
ErikHommel committed
94
95
96
97
98
99
100
101
102
103
104
105
  /**
   * Method to update gender of donor
   *
   * @param int $contactId
   * @param int $genderId
   * @return void
   */
  private function fixGender(int $contactId, int $genderId) {
    if ($contactId && $genderId) {
      if (function_exists('civicrm_api4')) {
        try {
          \Civi\Api4\Contact::update()
106
            ->setCheckPermissions(FALSE)
ErikHommel's avatar
ErikHommel committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
            ->addWhere('id', '=', $contactId)
            ->addValue('gender_id', $genderId)
            ->execute();
        }
        catch (API_Exception $ex) {
        }
      }
      else {
        try {
          civicrm_api3('Contact', 'create', [
            'contact_type' => "Individual",
            'id' => $contactId,
            'gender_id' => $genderId,
          ]);
        }
        catch (CiviCRM_API3_Exception $ex) {
        }
ErikHommel's avatar
ErikHommel committed
124
      }
ErikHommel's avatar
ErikHommel committed
125
    }
ErikHommel's avatar
ErikHommel committed
126
127
128
129
130
131
  }

  /**
   * Method to log koalect transaction
   *
   * @param array $apiData
ErikHommel's avatar
ErikHommel committed
132
   * @return int $max
ErikHommel's avatar
ErikHommel committed
133
134
   */
  public function log(array $apiData) {
135
    $logDate = new DateTime();
136
137
138
    $entity = $apiData['entity'];
    $action = $apiData['action'];
    unset($apiData['entity'], $apiData['action']);
ErikHommel's avatar
ErikHommel committed
139
    $data = json_encode($apiData);
ErikHommel's avatar
ErikHommel committed
140
141
142
    if (function_exists('civicrm_api4')) {
      try {
        \Civi\Api4\KoalectLog::create()
143
          ->setCheckPermissions(FALSE)
ErikHommel's avatar
ErikHommel committed
144
          ->addValue('date_received', $logDate->format('Y-m-d H:i:s'))
145
146
147
          ->addValue('request_data', $data)
          ->addValue('api_entity', $entity)
          ->addValue('api_action', $action)
ErikHommel's avatar
ErikHommel committed
148
149
150
151
152
          ->execute();
      }
      catch (API_Exception $ex) {
        Civi::log()->error(E::ts("Could not log incoming Koalect transaction with data :") . $data . E::ts(" in ") . __METHOD__);
      }
ErikHommel's avatar
ErikHommel committed
153
    }
ErikHommel's avatar
ErikHommel committed
154
    else {
155
      $insert = "INSERT INTO civicrm_koalect_log (date_received, request_data, api_action, api_entity) VALUES(%1, %2, %3, %4)";
ErikHommel's avatar
ErikHommel committed
156
157
158
      CRM_Core_DAO::executeQuery($insert, [
        1 => [$logDate->format('Y-m-d H:i:s'), "String"],
        2 => [$data, "String"],
159
160
        3 => [$action, "String"],
        4 => [$entity, "String"],
ErikHommel's avatar
ErikHommel committed
161
      ]);
ErikHommel's avatar
ErikHommel committed
162
    }
ErikHommel's avatar
ErikHommel committed
163
164
    $max = CRM_Core_DAO::singleValueQuery("SELECT MAX(id) FROM civicrm_koalect_log");
    return (int) $max;
165
166
  }

ErikHommel's avatar
ErikHommel committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  /**
   * Method to sanitize the incoming params into the right data set
   *
   * @param array $incomingParams
   * @return array
   */
  public function sanitizeIncomingParams(array $incomingParams) {
    $result = [
      'is_error' => TRUE,
      'error_message' => "No message name found",
      ];
    if (isset($incomingParams['message_name'])) {
      switch ($incomingParams['message_name']) {
        case "export_transaction":
          $result = $this->sanitizeExportTransaction($incomingParams);
          break;
        default:
          $result = [
            'is_error' => TRUE,
            'error_message' => "Message name" . $incomingParams['message_name'] . " not a valid transaction",
          ];
          break;
      }
    }
    return $result;
  }

  /**
   * Method to sanitize the incoming data for message export_transaction
   *
   * @param array $incomingParams
   * @return array
   */
  private function sanitizeExportTransaction(array $incomingParams) {
    $data = [];
    if (!empty($incomingParams)) {
      $data = $incomingParams;
ErikHommel's avatar
ErikHommel committed
204
      // amounts come in eurocent
ErikHommel's avatar
ErikHommel committed
205
      if (!empty($incomingParams['amount'])) {
ErikHommel's avatar
ErikHommel committed
206
207
        $amount = (int) $incomingParams['amount'];
        $amount = $amount / 100;
ErikHommel's avatar
ErikHommel committed
208
209
        $data['amount'] = round($amount, 2);
      }
ErikHommel's avatar
ErikHommel committed
210
      if (!empty($incomingParams['total_fee'])) {
ErikHommel's avatar
ErikHommel committed
211
212
        $totalFee = (int) $incomingParams['total_fee'];
        Civi::log()->debug('total fee is nu: ' . $totalFee);
ErikHommel's avatar
ErikHommel committed
213
214
215
        $totalFee = $totalFee / 100;
        $data['total_fee'] = round($totalFee, 2);
        Civi::log()->debug('total fee is na afloop: ' . $data['total_fee']);
ErikHommel's avatar
ErikHommel committed
216
      }
217
218
219
220
      if (!empty($incomingParams['stripe_payout_net'])) {
        $stripeNet = (int) $incomingParams['stripe_payout_net'] / 100;
        $data['stripe_payout_net'] = round($stripeNet, 2);
      }
ErikHommel's avatar
ErikHommel committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
      // take the dutch part of the campaign name and procedure name
      if (!empty($incomingParams['campaign_name'])) {
        if (isset($incomingParams['campaign_name']['nl-NL'])) {
          $data['campaign_name'] = $incomingParams['campaign_name']['nl-NL'];
        }
      }
      if (!empty($incomingParams['procedure_name'])) {
        if (isset($incomingParams['procedure_name']['nl-NL'])) {
          $data['procedure_name'] = $incomingParams['procedure_name']['nl-NL'];
        }
      }
      // translate status into correct civicrm contribution status
      if (!empty($incomingParams['status'])) {
        $data['status'] = $this->setContributionStatus($incomingParams['status']);
      }
      if (!empty($incomingParams['customer'])) {
        $this->setIncomingContact($data);
      }
ErikHommel's avatar
ErikHommel committed
239
240
241
      if (!empty($incomingParams['project_owner'])) {
        $this->setIncomingProjectOwner($data);
      }
ErikHommel's avatar
ErikHommel committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
      // check if the language exists in civicrm
      if (!empty($data['language'])) {
        if (!$this->existsLanguage($data['language'])) {
          unset($data['language']);
        }
      }
      // set default location type id
      $data['location_type_id'] = Civi::service('koalecttocivi')->getDefaultLocationTypeId();
      // translate country into the correct civicrm country id
      if (isset($data['country'])) {
        $this->setIncomingCountry($data);
      }
      // make sure date fields have correct format
      if (!empty($incomingParams['created_at'])) {
        $this->formatDate('created_at', 'd-m-Y H:i:s', $data);
      }
      if (!empty($incomingParams['should_arrive_at'])) {
        $this->formatDate('should_arrive_at', 'd-m-Y', $data);
      }
261
262
263
      if (!empty($incomingParams['provider_ids']) && is_array($incomingParams['provider_ids'])) {
        $data['provider_ids'] = json_encode($incomingParams['provider_ids']);
      }
ErikHommel's avatar
ErikHommel committed
264
265
266
267
    }
    return $data;
  }

ErikHommel's avatar
ErikHommel committed
268
269
270
271
272
273
274
275
276
277
278
279
  /**
   * Method to format the incoming date
   *
   * @param string $dateField
   * @param string $dateFormat
   * @param array $data
   * @return void
   * @throws Exception
   */
  private function formatDate(string $dateField, string $dateFormat, array &$data) {
    if (isset($data[$dateField])) {
      if (!$data[$dateField] instanceof DateTime) {
280
281
282
283
284
285
286
287
288
        try {
          $data[$dateField] = new DateTime($data[$dateField]);
        }
        catch (Exception $ex) {
          if (strlen($data[$dateField]) > 23) {
            $data[$dateField] = substr($data[$dateField], 0, 24);
            $data[$dateField] = new DateTime($data[$dateField]);
          }
        }
ErikHommel's avatar
ErikHommel committed
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
      }
      $data[$dateField] = $data[$dateField]->format($dateFormat);
    }
  }

  /**
   * Method to set the civicrm country id from the incoming iso code (or remove country if not found)
   *
   * @param array $data
   * @return void
   */
  private function setIncomingCountry(array &$data) {
    if (isset($data['country'])) {
      if (function_exists('civicrm_api4')) {
        try {
          $countries = \Civi\Api4\Country::get()
305
            ->setCheckPermissions(FALSE)
ErikHommel's avatar
ErikHommel committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
            ->addSelect('id')
            ->addWhere('iso_code', '=', $data['country'])
            ->execute();
          $country = $countries->first();
          if ($country['id']) {
            $data['country'] = (int) $country['id'];
          }
        }
        catch (API_Exception $ex) {
          unset($data['country']);
        }
      }
      else {
        try {
          $countryId = civicrm_api3('Country', 'getvalue', [
            'return' => "id",
            'iso_code' => $data['country'],
          ]);
          if ($countryId) {
            $data['country'] = (int) $countryId;
          }
        }
        catch (CiviCRM_API3_Exception $ex) {
          unset($data['country']);
        }
      }
    }
  }

  /**
   * Method to check if the incoming language exists in CiviCRM
   *
   * @param string $incomingLanguage
   * @return bool
   */
  private function existsLanguage(string $incomingLanguage) {
    if (function_exists('civicrm_api4')) {
      try {
        $optionValues = \Civi\Api4\OptionValue::get()
345
          ->setCheckPermissions(FALSE)
ErikHommel's avatar
ErikHommel committed
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
          ->addSelect('COUNT(*) AS count')
          ->addWhere('option_group_id:name', '=', 'languages')
          ->addWhere('name', '=', $incomingLanguage)
          ->execute();
        $language = $optionValues->first();
        if ($language['count'] && $language['count'] > 0) {
          return TRUE;
        }
      }
      catch (API_Exception $ex) {
      }
    }
    else {
      try {
        $count = civicrm_api3('OptionValue', 'getcount', [
          'option_group_id' => "languages",
          'name' => $incomingLanguage,
        ]);
        if ($count && $count > 0) {
          return TRUE;
        }
      }
      catch (CiviCRM_API3_Exception $ex) {
      }
    }
    return FALSE;
  }

ErikHommel's avatar
ErikHommel committed
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
  /**
   * Method to set the project owner data from the incoming project owner data
   * @param $data
   * @return void
   */
  private function setIncomingProjectOwner(&$data) {
    if (isset($data['project_owner'])) {
      $projectOwner = $data['project_owner'];
      $projectOwnerFields = ['email', 'firstname', 'lastname', 'id'];
      foreach ($projectOwnerFields as $projectOwnerField) {
        $data["project_owner_" . $projectOwnerField] = $projectOwner[$projectOwnerField];
      }
    }
    unset($data['project_owner']);
  }

ErikHommel's avatar
ErikHommel committed
390
391
392
393
394
395
396
397
398
399
400
401
402
  /**
   * Method to set the contact data from the incoming customer data
   *
   * @param array $data
   * @return void
   */
  public function setIncomingContact(array &$data) {
    if (isset($data['customer'])) {
      $customer = $data['customer'];
      // if there is an address, first set the address in data
      if (!empty($customer['address'])) {
        $this->setIncomingAddress($data);
      }
403
      $contactFields = ['email', 'firstname', 'lastname', 'newsletter', 'gender', 'iban'];
ErikHommel's avatar
ErikHommel committed
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
      foreach ($contactFields as $contactField) {
        $data[$contactField] = $customer[$contactField];
      }
      if (!empty($customer['birthday'])) {
        try {
          $birthDate = new DateTime($customer['birthday']);
          $data['birth_date'] = $birthDate->format('d-m-Y');
        }
        catch (Exception $ex) {
        }
      }
      unset ($data['customer']);
    }
  }

  /**
   * Method to set the contact address data from the incoming customer.address data
   * @param array $data
   * @return void
   */
  public function setIncomingAddress(array &$data) {
    if (!empty($data['customer']['address'])) {
      $address = $data['customer']['address'];
      $addressFields = [
        'city' => 'city',
        'line1' => 'street_address',
        'line2' => 'supplemental_address_line',
        'country' => 'country',
ErikHommel's avatar
ErikHommel committed
432
433
        'postal_code' => 'postal_code',
        'location_type_id' => Civi::service('koalecttocivi')->getDefaultLocationTypeId(),
ErikHommel's avatar
ErikHommel committed
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
      ];
      foreach ($addressFields as $koalect => $civi) {
        if (isset($address[$koalect])) {
          $data[$civi] = $address[$koalect];
        }
        else {
          $data[$civi] = "";
        }
      }
    }
  }

  /**
   * Method to set the CiviCRM contribution status from the koalect status
   *
   * @param string $incomingStatus
   * @return null
   */
  public function setContributionStatus(string $incomingStatus) {
    if ($incomingStatus == "succeeded") {
ErikHommel's avatar
ErikHommel committed
454
      $status = Civi::service('koalecttocivi')->getCompletedContributionStatusId();
ErikHommel's avatar
ErikHommel committed
455
456
    }
    else {
ErikHommel's avatar
ErikHommel committed
457
      $status = Civi::service('koalecttocivi')->getPendingContributionStatusId();
ErikHommel's avatar
ErikHommel committed
458
459
460
    }
    return $status;
  }
461
462
463
464
465
466
467

  /**
   * Method to prepare the result
   *
   * @param array $formProcessorResult
   * @return array
   */
ErikHommel's avatar
ErikHommel committed
468
469
  private function prepareResult(array $formProcessorResult) {
    $result = [];
470
    $resultFields = ['campaign_id', 'campaign_type_id', 'contact_id', 'contribution_id', 'soft_credit_id', 'transaction_id', 'gender_id'];
ErikHommel's avatar
ErikHommel committed
471
472
473
474
475
476
477
478
479
480
481
    if (isset($formProcessorResult['action'])) {
      foreach ($formProcessorResult['action'] as $actionData) {
        foreach ($actionData['output'] as $outputName => $outputValue) {
          if (in_array($outputName, $resultFields)) {
            $result[$outputName] = $outputValue;
          }
        }
      }
    }
    return $result;
  }
ErikHommel's avatar
ErikHommel committed
482

483
}