diff --git a/.drone.yml b/.drone.yml index 60d227fd9..b9b5a783e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -59,6 +59,7 @@ steps: - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/edit_mailing.spec.js - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_booster.spec.js + - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/report_check.spec.js - name: notify image: drillster/drone-email settings: @@ -143,6 +144,7 @@ steps: - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/edit_mailing.spec.js - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_booster.spec.js + - cd $DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/report_check.spec.js - name: notify image: drillster/drone-email settings: @@ -227,7 +229,9 @@ steps: - cd $DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/event_limit_approval_register.spec.js - cd $DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/event_unlimit_approval_register.spec.js - cd $DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/event_participant.spec.js + - cd $DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js - cd $DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/edit_mailing.spec.js + - cd $DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/report_check.spec.js - name: notify image: drillster/drone-email settings: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd12d3bda..9dc9ea804 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,8 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_allpay_atm.spec.js" - name: Payment Test - ALLPAY - Barcode - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_allpay_barcode.spec.js" + - name: Payment Test - SPGATEWAY - Playwright + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js" - name: Frontend - Contribution Editing - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/new_contribution.spec.js" - name: Frontend - Advanced Search - Playwright @@ -96,10 +98,10 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/event_participant.spec.js" - name: Frontend - Mailing Editing - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/edit_mailing.spec.js" - - name: Payment Test - SPGATEWAY - Playwright - run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js" - name: Frontend - Contribution Booster - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_booster.spec.js" + - name: Frontend - Report Page Checking - Playwright + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/report_check.spec.js" - uses: actions/upload-artifact@v3 if: always() @@ -179,6 +181,8 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_allpay_atm.spec.js" - name: Payment Test - ALLPAY - Barcode - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_allpay_barcode.spec.js" + - name: Payment Test - SPGATEWAY - Playwright + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js" - name: Frontend - Contribution Editing - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/new_contribution.spec.js" - name: Frontend - Advanced Search - Playwright @@ -203,10 +207,10 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/event_participant.spec.js" - name: Frontend - Mailing Editing - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/edit_mailing.spec.js" - - name: Payment Test - SPGATEWAY - Playwright - run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js" - name: Frontend - Contribution Booster - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_booster.spec.js" + - name: Frontend - Report Page Checking - Playwright + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/sites/all/modules/civicrm/tests/playwright/ && npx playwright test tests/report_check.spec.js" - uses: actions/upload-artifact@v3 if: always() @@ -248,6 +252,8 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/phpunit && phpunit --filter testLastReceiptId CRM/Contribute/BAO/ContributionTest.php" - name: Payment Test - ALLPAY run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/phpunit && phpunit CRM/Core/Payment/ALLPAYTest.php" + - name: Payment Test - Neweb(new) + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/phpunit && phpunit CRM/Core/Payment/SPGATEWAYTest.php" - name: Payment Test - LINE Pay run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/phpunit && phpunit CRM/Core/Payment/LinePayTest.php" - name: Payment Test - TapPay @@ -286,6 +292,8 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_allpay_atm.spec.js" - name: Payment Test - ALLPAY - Barcode - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_allpay_barcode.spec.js" + - name: Payment Test - SPGATEWAY - Playwright + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_spgateway.spec.js" - name: Frontend - Contribution Editing - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/new_contribution.spec.js" - name: Frontend - Advanced Search - Playwright @@ -312,6 +320,8 @@ jobs: run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/edit_mailing.spec.js" - name: Frontend - Contribution Booster - Playwright run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/contribution_booster.spec.js" + - name: Frontend - Report Page Checking - Playwright + run: docker exec neticrm-ci bash -c "cd \$DRUPAL_ROOT/modules/civicrm/tests/playwright/ && npx playwright test tests/report_check.spec.js" diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 7992ad39d..25ae846d9 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -2604,17 +2604,17 @@ static function makeNotifyUrl(&$params, $path, $return_query = FALSE){ $query[] = "contact_id={$params['contactID']}"; $query[] = "cid={$params['contributionID']}"; - if($params['eventID']) { + if(!empty($params['eventID'])) { $query[] = "module=event"; $query[] = "eid={$params['eventID']}"; $query[] = "pid={$params['participantID']}"; } else { $query[] = "module=contribute"; - if ( $params['membershipID'] ) { + if (!empty($params['membershipID'])) { $query[] = "mid=".$params['membershipID']; } - if ($params['related_contact']) { + if (!empty($params['related_contact'])) { $query[] = "rid=".$params['related_contact']; if ($params['onbehalf_dupe_alert']) { $query[] = "onbehalf_dupe_alert=".$params['onbehalf_dupe_alert']; diff --git a/CRM/Contribute/DAO/SPGATEWAY.php b/CRM/Contribute/DAO/SPGATEWAY.php new file mode 100644 index 000000000..801b381c1 --- /dev/null +++ b/CRM/Contribute/DAO/SPGATEWAY.php @@ -0,0 +1,235 @@ + 'civicrm_contribution:id', + ); + } + return self::$_links; + } + /** + * Returns foreign keys and entity references. + * + * @return array + * [CRM_Core_Reference_Interface] + */ + public static function getReferenceColumns() + { + if (!isset(Civi::$statics[__CLASS__]['links'])) { + Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'cid', 'civicrm_contribution', 'id'); + } + return Civi::$statics[__CLASS__]['links']; + } + /** + * returns all the column names of this table + * + * @access public + * @return array + */ + static function &fields() + { + if (!(self::$_fields)) { + self::$_fields = array( + 'id' => array( + 'name' => 'id', + 'type' => CRM_Utils_Type::T_INT, + 'required' => true, + ) , + 'spgateway_contribution_id' => array( + 'name' => 'cid', + 'type' => CRM_Utils_Type::T_INT, + 'title' => ts('Spgateway Contribution ID') , + 'FKClassName' => 'CRM_Contribute_DAO_Contribution', + ) , + 'data' => array( + 'name' => 'data', + 'type' => CRM_Utils_Type::T_BLOB, + 'title' => ts('Data') , + ) , + ); + } + return self::$_fields; + } + /** + * returns the names of this table + * + * @access public + * @return string + */ + static function getTableName() + { + return self::$_tableName; + } + /** + * returns if this table needs to be logged + * + * @access public + * @return boolean + */ + function getLog() + { + return self::$_log; + } + /** + * returns the list of fields that can be imported + * + * @access public + * return array + */ + static function &import($prefix = false) + { + if (!(self::$_import)) { + self::$_import = array(); + $fields = &self::fields(); + foreach($fields as $name => $field) { + if (CRM_Utils_Array::value('import', $field)) { + if ($prefix) { + self::$_import['contribution_spgateway'] = &$fields[$name]; + } else { + self::$_import[$name] = &$fields[$name]; + } + } + } + } + return self::$_import; + } + /** + * returns the list of fields that can be exported + * + * @access public + * return array + */ + static function &export($prefix = false) + { + if (!(self::$_export)) { + self::$_export = array(); + $fields = &self::fields(); + foreach($fields as $name => $field) { + if (CRM_Utils_Array::value('export', $field)) { + if ($prefix) { + self::$_export['contribution_spgateway'] = &$fields[$name]; + } else { + self::$_export[$name] = &$fields[$name]; + } + } + } + } + return self::$_export; + } +} diff --git a/CRM/Contribute/Form/ContributionPage/Amount.php b/CRM/Contribute/Form/ContributionPage/Amount.php index 63b1f7745..1dfc287b3 100644 --- a/CRM/Contribute/Form/ContributionPage/Amount.php +++ b/CRM/Contribute/Form/ContributionPage/Amount.php @@ -224,7 +224,7 @@ private static function doShowHideFrequencyUnits(&$recurFrequencyUnits, $recurri foreach ($recurringPaymentProcessor as $ppid) { $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($ppid, ''); $paymentClass = 'CRM_Core_'.$paymentProcessor['class_name']; - if (!empty($paymentClass::$_allowRecurUnit)) { + if (class_exists($paymentClass) && property_exists($paymentClass, '_allowRecurUnit')) { $paymentUnitsCount[$unit] += in_array($unit, $paymentClass::$_allowRecurUnit) ? 1 : 0; } else { diff --git a/CRM/Contribute/Form/ContributionRecur.php b/CRM/Contribute/Form/ContributionRecur.php index a9f258577..2734e23d4 100644 --- a/CRM/Contribute/Form/ContributionRecur.php +++ b/CRM/Contribute/Form/ContributionRecur.php @@ -352,9 +352,9 @@ public function postProcess() { ); // refs #17486. Date format should be YmdHis. - foreach ($params as $key => $value) { - if(preg_match('/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/',$value)){ - $params[$key] = preg_replace('/-| |:/', '', $value); + foreach (array('create_date', 'start_date', 'modified_date', 'cancel_date', 'end_date', 'next_sched_contribution', 'failure_retry_date', 'last_execute_date') as $idx) { + if(preg_match('/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/', $params[$idx])){ + $params[$idx] = preg_replace('/-| |:/', '', $params[$idx]); } } diff --git a/CRM/Core/BAO/Address.php b/CRM/Core/BAO/Address.php index 39138dce7..11d46c516 100644 --- a/CRM/Core/BAO/Address.php +++ b/CRM/Core/BAO/Address.php @@ -518,7 +518,7 @@ function addDisplay($microformat = FALSE) { // added this for CRM 1200 'address_id' => $this->id, // CRM-4003 - 'address_name' => str_replace('', ' ', $this->name), + 'address_name' => !empty($this->_name) ? str_replace('', ' ', $this->name) : '', 'street_address' => $this->street_address, 'supplemental_address_1' => $this->supplemental_address_1, 'supplemental_address_2' => $this->supplemental_address_2, diff --git a/CRM/Core/BAO/PaymentProcessor.php b/CRM/Core/BAO/PaymentProcessor.php index 4ef914c25..099191f4b 100644 --- a/CRM/Core/BAO/PaymentProcessor.php +++ b/CRM/Core/BAO/PaymentProcessor.php @@ -183,7 +183,7 @@ static function getPayments($paymentProcessorIDs, $mode) { $dao->id = $paymentProcessorID; $dao->is_active = 1; if (!$dao->find(TRUE)) { - return NULL; + continue; } if ($mode == 'test') { @@ -211,6 +211,9 @@ static function getPayments($paymentProcessorIDs, $mode) { } } $paymentDAO = $paymentDefault + $paymentDAO; + if (empty($paymentDAO)) { + return NULL; + } return $paymentDAO; } diff --git a/CRM/Core/BAO/UFGroup.php b/CRM/Core/BAO/UFGroup.php index 6c6f2f77d..6c23e2c60 100644 --- a/CRM/Core/BAO/UFGroup.php +++ b/CRM/Core/BAO/UFGroup.php @@ -2401,7 +2401,7 @@ static function commonSendMail($contactID, &$values) { * @return array * @access public */ - function checkFieldsEmptyValues($gid, $cid, $params) { + public static function checkFieldsEmptyValues($gid, $cid, $params) { if ($gid) { require_once 'CRM/Core/BAO/UFGroup.php'; if (CRM_Core_BAO_UFGroup::filterUFGroups($gid, $cid)) { @@ -2438,7 +2438,7 @@ function checkFieldsEmptyValues($gid, $cid, $params) { * @return void * @access public */ - function profileDisplay($gid, $values, $template) { + public static function profileDisplay($gid, $values, $template) { $groupTitle = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'title'); $template->assign("grouptitle", $groupTitle); if (count($values)) { diff --git a/CRM/Core/Config/Variables.php b/CRM/Core/Config/Variables.php index 4b8b9e3a1..62e08d23d 100644 --- a/CRM/Core/Config/Variables.php +++ b/CRM/Core/Config/Variables.php @@ -443,10 +443,17 @@ class CRM_Core_Config_Variables extends CRM_Core_Config_Defaults { public $cspRules = ""; /** - * AI Organization profile + * AI Organization profile */ public $aiOrganizationIntro = ''; + /** + * Webserver Log Dir + * + * It should only use for web server log processing cron jobs + */ + public $webLogDir = 'log'; + /** * Provide addressSequence * diff --git a/CRM/Core/DAO/.listAll.php b/CRM/Core/DAO/.listAll.php index 2242a1306..f12ef2ae5 100644 --- a/CRM/Core/DAO/.listAll.php +++ b/CRM/Core/DAO/.listAll.php @@ -143,6 +143,7 @@ $dao['TapPay'] = 'CRM_Contribute_DAO_TapPay'; $dao['TapPayLog'] = 'CRM_Contribute_DAO_TapPayLog'; $dao['AllPay'] = 'CRM_Contribute_DAO_AllPay'; +$dao['SPGATEWAY'] = 'CRM_Contribute_DAO_SPGATEWAY'; $dao['CouponTrack'] = 'CRM_Coupon_DAO_CouponTrack'; $dao['MembershipPayment'] = 'CRM_Member_DAO_MembershipPayment'; $dao['Event'] = 'CRM_Event_DAO_Event'; diff --git a/CRM/Core/DAO/AllCoreTables.data.php b/CRM/Core/DAO/AllCoreTables.data.php index 5b2a7b9c1..28f23a91f 100644 --- a/CRM/Core/DAO/AllCoreTables.data.php +++ b/CRM/Core/DAO/AllCoreTables.data.php @@ -716,6 +716,11 @@ 'class' => 'CRM_Contribute_DAO_AllPay', 'table' => 'civicrm_contribution_allpay', ) , + 'CRM_Contribute_DAO_SPGATEWAY' => array( + 'name' => 'SPGATEWAY', + 'class' => 'CRM_Contribute_DAO_SPGATEWAY', + 'table' => 'civicrm_contribution_spgateway', + ) , 'CRM_Coupon_DAO_CouponTrack' => array( 'name' => 'CouponTrack', 'class' => 'CRM_Coupon_DAO_CouponTrack', diff --git a/CRM/Core/I18n.php b/CRM/Core/I18n.php index 708b344c9..9e2d771a9 100644 --- a/CRM/Core/I18n.php +++ b/CRM/Core/I18n.php @@ -301,7 +301,7 @@ function crm_translate($text, $params = array()) { } // replace the numbered %1, %2, etc. params if present - if (count($params)) { + if (is_array($params) && count($params)) { $text = $this->strarg($text, $params); } diff --git a/CRM/Core/Payment/ALLPAY.php b/CRM/Core/Payment/ALLPAY.php index 5db898b91..42bc7e22d 100644 --- a/CRM/Core/Payment/ALLPAY.php +++ b/CRM/Core/Payment/ALLPAY.php @@ -758,7 +758,7 @@ public static function recurCheck($rid, $order = NULL) { } // manually trigger ipn - self::doIPN('Credit', $post, $get, FALSE); + self::doIPN(array('allpay', 'ipn', 'Credit'), $post, $get, FALSE); } } } @@ -844,7 +844,7 @@ public static function tradeCheck($orderId, $order = NULL) { $ipnPost['do_not_email'] = 1; } */ - $result = self::doIPN('Credit', $ipnPost, $ipnGet, FALSE); + $result = self::doIPN(array('allpay', 'ipn', 'Credit'), $ipnPost, $ipnGet, FALSE); return $result; } } @@ -985,17 +985,25 @@ function cancelRecuringMessage($recurID){ /** * Execute ipn as called from allpay transaction. Original civicrm_allpay_ipn * - * @param string $instrument The code of used instrument like 'Credit' or 'ATM'. + * @param array $instrument The code of used instrument like 'Credit' or 'ATM'. * @param array $post Bring post variables if you need test. * @param array $get Bring get variables if you need test. * @param boolean $print Does server echo the result, or just return that. Default is TRUE. * * @return string|void If $print is FALSE, function will return the result as Array. */ - static function doIPN($instrument = NULL, $post = NULL, $get = NULL, $print = TRUE) { + static function doIPN($arguments, $post = NULL, $get = NULL, $print = TRUE) { // detect variables $post = !empty($post) ? $post : $_POST; $get = !empty($get) ? $get : $_GET; + if (!empty($arguments)) { + if (is_array($arguments)) { + $instrument = end($arguments); + } + else { + $instrument = $arguments; + } + } if (empty($instrument)) { $qArray = explode('/', $get['q']); $instrument = end($qArray); @@ -1004,13 +1012,11 @@ static function doIPN($instrument = NULL, $post = NULL, $get = NULL, $print = TR // detect variables if(empty($post)){ CRM_Core_Error::debug_log_message( "civicrm_allpay: Could not find POST data from payment server", TRUE); - exit; + CRM_Utils_System::civiExit(); } else{ $component = $get['module']; if(!empty($component)){ - // include_once(__DIR__.'/ALLPAYIPN.php'); - $ipn = new CRM_Core_Payment_ALLPAYIPN($post, $get); $result = $ipn->main($component, $instrument); if(!empty($result) && $print){ @@ -1024,6 +1030,7 @@ static function doIPN($instrument = NULL, $post = NULL, $get = NULL, $print = TR CRM_Core_Error::debug_log_message( "civicrm_allpay: Could not get module name from request url", TRUE); } } + CRM_Utils_System::civiExit(); } } diff --git a/CRM/Core/Payment/ALLPAYIPN.php b/CRM/Core/Payment/ALLPAYIPN.php index b631b7f39..b5b9faad9 100644 --- a/CRM/Core/Payment/ALLPAYIPN.php +++ b/CRM/Core/Payment/ALLPAYIPN.php @@ -17,7 +17,12 @@ function main($component, $instrument){ $objects = $ids = $input = array(); $input = $this->_post; $this->getIds($ids, $component); - $input['component'] = $component; + if (!empty($ids['participant'])) { + $input['component'] = 'event'; + } + else { + $input['component'] = 'contribute'; + } $qfKey = CRM_Utils_Array::value('qfKey', $this->_get); $civi_base_url = $component == 'event' ? 'civicrm/event/register' : 'civicrm/contribute/transact'; @@ -81,19 +86,22 @@ function main($component, $instrument){ // never for front-end user. } - function getIds( &$ids , $component){ - $ids['contact'] = CRM_Utils_Array::value('contact_id', $this->_get); - $ids['contribution'] = CRM_Utils_Array::value('cid', $this->_get); - if ( $component == 'event' ) { - $ids['event'] = CRM_Utils_Array::value('eid', $this->_get); - $ids['participant'] = CRM_Utils_Array::value('pid', $this->_get); + function getIds(&$ids){ + $contribId = CRM_Utils_Array::value('cid', $this->_get); + if (!empty($contribId) && CRM_Utils_Type::escape($contribId, 'Integer')) { + $ids = CRM_Contribute_BAO_Contribution::buildIds($contribId, FALSE); + if (empty($ids)) { + CRM_Core_Error::debug_log_message("Allpay: Could not found contribution id $contribId"); + CRM_Utils_System::civiExit(); + } } - else { - $ids['membership'] = CRM_Utils_Array::value('mid', $this->_get); - $ids['contributionRecur'] = CRM_Utils_Array::value('crid', $this->_get); - $ids['contributionPage'] = CRM_Utils_Array::value('cpid', $this->_get); - $ids['related_contact'] = CRM_Utils_Array::value('rid', $this->_get); - $ids['onbehalf_dupe_alert'] = CRM_Utils_Array::value('onbehalf_dupe_alert', $this->_get); + if (!empty($ids['participant'])) { + if (!empty($this->_get['rid'])) { + $ids['related_contact'] = CRM_Utils_Array::value('rid', $this->_get); + } + if (!empty($this->_get['onbehalf_dupe_alert'])) { + $ids['onbehalf_dupe_alert'] = CRM_Utils_Array::value('onbehalf_dupe_alert', $this->_get); + } } } diff --git a/CRM/Core/Payment/Backer.php b/CRM/Core/Payment/Backer.php index 4d0cfae36..e11984691 100644 --- a/CRM/Core/Payment/Backer.php +++ b/CRM/Core/Payment/Backer.php @@ -320,7 +320,8 @@ function processContribution($jsonString, &$contributionResult) { 'email' => $params['additional']['email'][0]['email'], 'last_name' => $params['additional']['last_name'], 'first_name' => $params['additional']['first_name'], - 'phone' => $params['additional']['phone'][0]['phone'] + 'phone' => $params['additional']['phone'][0]['phone'], + 'street_address' => $params['additional']['address'][0]['street_address'], ); $dedupeParams = CRM_Dedupe_Finder::formatParams($dedupeParams, 'Individual'); $foundDupes = CRM_Dedupe_Finder::dupesByRules( @@ -333,6 +334,7 @@ function processContribution($jsonString, &$contributionResult) { array('table' => 'civicrm_contact', 'field' => 'first_name', 'weight' => 8), array('table' => 'civicrm_email', 'field' => 'email', 'weight' => 10), array('table' => 'civicrm_phone', 'field' => 'phone', 'weight' => 7), + array('table' => 'civicrm_address', 'field' => 'street_address', 'weight' => 8), ), 20 ); diff --git a/CRM/Core/Payment/Mobile.php b/CRM/Core/Payment/Mobile.php index 0bef1ee14..685ff907b 100644 --- a/CRM/Core/Payment/Mobile.php +++ b/CRM/Core/Payment/Mobile.php @@ -151,22 +151,21 @@ function doTransferCheckout(&$params, $component) { ); CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution SET payment_instrument_id = %1 WHERE id = %2", $options); } + CRM_Core_Error::debug_var('mobile_payment_params', $params); if($this->_instrumentType == 'linepay'){ + CRM_Core_Error::debug_var('mobile_payment_linepay', $cid); $this->_mobilePayment = new CRM_Core_Payment_LinePay($params['payment_processor']); $this->_mobilePayment->doRequest($params); return; } + CRM_Core_Error::debug_var('mobile_payment_others', $cid); // If not use linepay, We need another payment processor. $qfKey = $params['qfKey']; $paymentProcessor = $this->_paymentProcessor; $provider_name = $paymentProcessor['password']; - $module_name = 'civicrm_'.strtolower($provider_name); - if ($this->_instrumentType != 'linepay' && module_load_include('inc', $module_name, $module_name.'.checkout') === FALSE) { - CRM_Core_Error::fatal('Module '.$module_name.' doesn\'t exists.'); - } if(!empty($params['eventID'])){ $event = new CRM_Event_DAO_Event(); @@ -213,11 +212,11 @@ function doTransferCheckout(&$params, $component) { print($page); CRM_Utils_System::civiExit(); } - else if ($this->_instrumentType == 'googlepay') { - $checkoutFunction = $module_name.'_do_transfer_checkout'; + else if ($this->_instrumentType == 'googlepay' && $provider_name == 'spgateway') { $mode = $is_test ? 'test':''; $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($this->_paymentProcessor['user_name'], $mode); - $checkoutFunction($params, $component, $paymentProcessor, $is_test); + $payment = new CRM_Core_Payment_SPGATEWAY($mode, $paymentProcessor); + $payment->doTransferCheckout($params, $component); } } @@ -344,12 +343,11 @@ static function transact(){ $type = 'applepay'; } // call mobile checkout function - $module_name = 'civicrm_'.strtolower($ppProvider); - $checkout_func = $module_name.'_mobile_checkout'; - if(!function_exists($checkout_func)){ - return CRM_Core_Error::fatal('Function '.$checkout_func.' doesn\'t exists.'); + $paymentProviderClass = 'CRM_Core_Payment_'.strtoupper($ppProvider); + if (!is_callable(array($paymentProviderClass, 'mobileCheckout'))) { + return CRM_Core_Error::fatal('Function '.$paymentProviderClass.'::mobileCheckout doesn\'t exists.'); } - $return = call_user_func($checkout_func, $type, $post, $objects); + $return = call_user_func(array($paymentProviderClass, 'mobileCheckout'), $type, $post, $objects); if(!empty($return)){ diff --git a/CRM/Core/Payment/SPGATEWAY.php b/CRM/Core/Payment/SPGATEWAY.php index 3af812f19..7344af722 100644 --- a/CRM/Core/Payment/SPGATEWAY.php +++ b/CRM/Core/Payment/SPGATEWAY.php @@ -2,6 +2,18 @@ date_default_timezone_set('Asia/Taipei'); require_once 'CRM/Core/Payment.php'; class CRM_Core_Payment_SPGATEWAY extends CRM_Core_Payment { + const EXPIRE_DAY = 7; + const MAX_EXPIRE_DAY = 180; + const RESPONSE_TYPE = 'JSON'; + const MPG_VERSION = '1.2'; + const RECUR_VERSION = '1.0'; + const QUERY_VERSION = '1.1'; + const REAL_DOMAIN = 'https://core.newebpay.com'; + const TEST_DOMAIN = 'https://ccore.newebpay.com'; + const URL_SITE = '/MPG/mpg_gateway'; + const URL_API = '/API/QueryTradeInfo'; + const URL_RECUR = '/MPG/period'; + const URL_CREDITBG = "/API/CreditCard"; /** * mode of operation: live or test @@ -9,7 +21,7 @@ class CRM_Core_Payment_SPGATEWAY extends CRM_Core_Payment { * @var object * @static */ - static protected $_mode = NULL; + protected static $_mode = NULL; public static $_hideFields = array('invoice_id'); @@ -37,7 +49,10 @@ class CRM_Core_Payment_SPGATEWAY extends CRM_Core_Payment { * @var object * @static */ - static private $_singleton = NULL; + private static $_singleton = NULL; + + private $_config = NULL; + private $_processorName = NULL; /** * Constructor @@ -54,7 +69,7 @@ function __construct($mode, &$paymentProcessor) { $this->_config = $config; } - static function getEditableFields($paymentProcessor = NULL, $form = NULL) { + public static function getEditableFields($paymentProcessor = NULL, $form = NULL) { if (empty($paymentProcessor)) { $returnArray = array(); } @@ -89,7 +104,7 @@ static function getEditableFields($paymentProcessor = NULL, $form = NULL) { return $returnArray; } - static function postBuildForm($form) { + public static function postBuildForm($form) { $form->addDate('cycle_day_date', FALSE, FALSE, array('formatType' => 'custom', 'format' => 'mm-dd')); $cycleDay = &$form->getElement('cycle_day'); unset($cycleDay->_attributes['max']); @@ -103,7 +118,7 @@ static function postBuildForm($form) { } } - static function validateInstallments($fields, $ignore, $form) { + public static function validateInstallments($fields, $ignore, $form) { $errors = array(); $pass = TRUE; $contribution_status_id = $fields['contribution_status_id']; @@ -127,7 +142,7 @@ static function validateInstallments($fields, $ignore, $form) { * @static * */ - static function &singleton($mode, &$paymentProcessor, &$paymentForm = NULL) { + public static function &singleton($mode, &$paymentProcessor, &$paymentForm = NULL) { $processorName = $paymentProcessor['name']; if (self::$_singleton[$processorName] === NULL) { self::$_singleton[$processorName] = new CRM_Core_Payment_SPGATEWAY($mode, $paymentProcessor); @@ -192,49 +207,417 @@ function doTransferCheckout(&$params, $component) { if ($component != 'contribute' && $component != 'event') { CRM_Core_Error::fatal(ts('Component is invalid')); } - if (module_load_include('inc', 'civicrm_spgateway', 'civicrm_spgateway.checkout') === FALSE) { - CRM_Core_Error::fatal('Module civicrm_spgateway doesn\'t exists.'); + $is_test = $this->_mode == 'test' ? 1 : 0; + if (isset($this->_paymentForm) && get_class($this->_paymentForm) == 'CRM_Contribute_Form_Payment_Main') { + if (empty($params['email-5'])) { + // Retrieve email of billing type or primary. + $locationTypes = CRM_Core_PseudoConstant::locationType(FALSE, 'name'); + $bltID = array_search('Billing', $locationTypes); + if (!$bltID) { + return CRM_Core_Error::statusBounce(ts('Please set a location type of %1', array(1 => 'Billing'))); + } + $fields = array(); + $fields['email-'.$bltID] = 1; + $fields['email-Primary'] = 1; + $default = array(); + + CRM_Core_BAO_UFGroup::setProfileDefaults($params['contactID'], $fields, $default); + if (!empty($default['email-'.$bltID])) { + $params['email-5'] = $default['email-'.$bltID]; + } + elseif (!empty($default['email-Primary'])) { + $params['email-5'] = $default['email-Primary']; + } + } + $params['item_name'] = $params['description']; } - else { - $is_test = $this->_mode == 'test' ? 1 : 0; - if (isset($this->_paymentForm) && get_class($this->_paymentForm) == 'CRM_Contribute_Form_Payment_Main') { - if (empty($params['email-5'])) { - // Retrieve email of billing type or primary. - $locationTypes = CRM_Core_PseudoConstant::locationType(FALSE, 'name'); - $bltID = array_search('Billing', $locationTypes); - if (!$bltID) { - return CRM_Core_Error::statusBounce(ts('Please set a location type of %1', array(1 => 'Billing'))); - } - $fields = array(); - $fields['email-'.$bltID] = 1; - $fields['email-Primary'] = 1; - $default = array(); - - CRM_Core_BAO_UFGroup::setProfileDefaults($params['contactID'], $fields, $default); - if (!empty($default['email-'.$bltID])) { - $params['email-5'] = $default['email-'.$bltID]; - } - elseif (!empty($default['email-Primary'])) { - $params['email-5'] = $default['email-Primary']; + + $instrumentId = $params['civicrm_instrument_id']; + $options = array(1 => array( $instrumentId, 'Integer')); + $instrumentName = CRM_Core_DAO::singleValueQuery("SELECT v.name FROM civicrm_option_value v INNER JOIN civicrm_option_group g ON v.option_group_id = g.id WHERE g.name = 'payment_instrument' AND v.is_active = 1 AND v.value = %1;", $options); + $spgatewayInstruments = self::instruments('code'); + $instrumentCode = $spgatewayInstruments[$instrumentName]; + if (empty($instrumentCode)) { + // For google pay + $instrumentCode = $instrumentName; + } + $formKey = $component == 'event' ? 'CRM_Event_Controller_Registration_'.$params['qfKey'] : 'CRM_Contribute_Controller_Contribution_'.$params['qfKey']; + + // The first, we insert every contribution into record. After this, we'll use update for the record. + $exists = CRM_Core_DAO::singleValueQuery("SELECT cid FROM civicrm_contribution_spgateway WHERE cid = %1", array( + 1 => array($params['contributionID'], 'Integer'), + )); + if (!$exists) { + CRM_Core_DAO::executeQuery("INSERT INTO civicrm_contribution_spgateway (cid) VALUES (%1)", array( + 1 => array($params['contributionID'], 'Integer'), + )); + } + + if($instrumentCode == 'Credit' || $instrumentCode == 'WebATM'){ + $isPayLater = FALSE; + } + else{ + $isPayLater = TRUE; + + // Set participant status to 'Pending from pay later', Accupied the seat. + if($params['participantID']){ + $participantStatus = CRM_Event_PseudoConstant::participantStatus(); + if($newStatus = array_search('Pending from pay later', $participantStatus)){ + CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Participant', $params['participantID'], 'status_id', $newStatus, 'id'); + $cancelledStatus = array_search('Cancelled', $participantStatus); + $sql = 'SELECT id FROM civicrm_participant WHERE registered_by_id = %1 AND status_id != %2'; + $paramsRegisteredBy = array( + 1 => array($params['participantID'], 'Integer'), + 2 => array($cancelledStatus, 'Integer'), + ); + $dao = CRM_Core_DAO::executeQuery($sql, $paramsRegisteredBy); + while($dao->fetch()){ + CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Participant', $dao->id, 'status_id', $newStatus, 'id'); } } - $params['item_name'] = $params['description']; } - civicrm_spgateway_do_transfer_checkout($params, $component, $this->_paymentProcessor, $is_test); } + + // now process contribution to save some default value + $contrib_params = array( 'id' => $params['contributionID'] ); + $contrib_values = $contrib_ids = array(); + CRM_Contribute_BAO_Contribution::getValues($contrib_params, $contrib_values, $contrib_ids); + if($params['civicrm_instrument_id']){ + $contrib_values['payment_instrument_id'] = $params['civicrm_instrument_id']; + } + $contrib_values['is_pay_later'] = $isPayLater; + $contrib_values['trxn_id'] = self::generateTrxnId($is_test, $params['contributionID']); + $contribution =& CRM_Contribute_BAO_Contribution::create($contrib_values, $contrib_ids); + + // Inject in quickform sessions + // Special hacking for display trxn_id after thank you page. + $_SESSION['CiviCRM'][$formKey]['params']['trxn_id'] = $contribution->trxn_id; + $_SESSION['CiviCRM'][$formKey]['params']['is_pay_later'] = $isPayLater; + $params['trxn_id'] = $contribution->trxn_id; + + $arguments = $this->prepareOrderParams($contribution, $params, $instrumentCode, $formKey); + if(!$contrib_values['is_recur']){ + CRM_Core_Payment_SPGATEWAYAPI::checkMacValue($arguments, $this->_paymentProcessor); + } + CRM_Core_Error::debug_var('spgateway_post_data_', $arguments); + /* TODO: detect this sh*t + // making redirect form + $alter = array( + 'module' => 'civicrm_spgateway', + 'billing_mode' => $this->_paymentProcessor['billing_mode'], + 'params' => $arguments, + ); + drupal_alter('civicrm_checkout_params', $alter); + */ + print $this->redirectForm($arguments); + CRM_Utils_System::civiExit(); } + /** + * Migrate from _civicrm_spgateway_order + * + * Prepare order form element + * + * @param object $contribution + * @param array $vars + * @param object $paymentProcessor + * @param string $instrumentCode + * @param string $formKey + * @return void + */ + function prepareOrderParams(&$contribution, &$vars, $instrumentCode, $formKey){ + global $tsLocale; + + // url + $ids = CRM_Contribute_BAO_Contribution::buildIds($contribution->id); + $notifyURL= CRM_Contribute_BAO_Contribution::makeNotifyUrl($ids, 'spgateway/ipn/'.$instrumentCode); + $baseURL= CRM_Utils_System::currentPath(); + $urlParams = array( "_qf_ThankYou_display" => "1" , "qfKey" => $vars['qfKey'], ); + $thankyouURL = CRM_Utils_System::url($baseURL, http_build_query($urlParams), TRUE); + + $component = !empty($ids['eventID']) ? 'event' : 'contribution'; + + // parameter + if($component == 'event' && !empty($_SESSION['CiviCRM'][$formKey])){ + $values =& $_SESSION['CiviCRM'][$formKey]['values']['event']; + } + else{ + $values =& $_SESSION['CiviCRM'][$formKey]['values']; + } + + // max 180 days of expire + $baseTime = time() + 86400; // because not include today + if (!empty($vars['payment_expired_timestamp'])) { + $hours = ($vars['payment_expired_timestamp'] - $baseTime) / 3600; + } + else { + $hours = (CRM_Core_Payment::calcExpirationDate(0) - $baseTime) / 3600; + } + if ($hours < 24) { + $values['expiration_day'] = 1; + } + elseif ($hours > 24 * self::MAX_EXPIRE_DAY ) { + $values['expiration_day'] = self::MAX_EXPIRE_DAY; + } + elseif(!empty($hours)){ + $values['expiration_day'] = ceil($hours/24); + } + + // building vars + $amount = $vars['currencyID'] == 'TWD' && strstr($vars['amount'], '.') ? substr($vars['amount'], 0, strpos($vars['amount'],'.')) : $vars['amount']; + + $itemDescription = $vars['description']; + $itemDescription .= ($vars['description'] == $vars['item_name'])?'':':'.$vars['item_name']; + $itemDescription .= ':'.floatval($vars['amount']); + $itemDescription = preg_replace('/[^[:alnum:][:space:]]/u', ' ', $itemDescription); + + if(!$vars['is_recur']){ + $args = array( + 'MerchantID' => $this->_paymentProcessor['user_name'], + 'RespondType' => self::RESPONSE_TYPE, + 'TimeStamp' => time(), + 'Version' => self::MPG_VERSION, + 'Amt' => $amount, + 'NotifyURL' => $notifyURL, + 'Email' => $vars['email-5'], + 'LoginType' => '0', + 'ItemDesc' => $itemDescription, + 'MerchantOrderNo' => $vars['trxn_id'], + ); + if ($this->_paymentProcessor['is_test']) { + $args['#url'] = self::TEST_DOMAIN.self::URL_SITE; + } + else { + $args['#url'] = self::REAL_DOMAIN.self::URL_SITE; + } + + switch($instrumentCode){ + case 'ATM': + $args['VACC'] = 1; + $day = !empty($values['expiration_day']) ? $values['expiration_day'] : self::EXPIRE_DAY; + $args['ExpireDate'] = date('Ymd',strtotime("+$day day")); + $args['CustomerURL'] = $thankyouURL; + // $args['ReturnURL'] = url('spgateway/record/'.$vars['contributionID'], array('absolute' => true)); + break; + case 'BARCODE': + $args['BARCODE'] = 1; + $day = !empty($values['expiration_day']) ? $values['expiration_day'] : self::EXPIRE_DAY; + $args['ExpireDate'] = date('Ymd',strtotime("+$day day")); + $args['CustomerURL'] = $thankyouURL; + // $args['ReturnURL'] = url('spgateway/record/'.$vars['contributionID'], array('absolute' => true)); + break; + case 'CVS': + $args['CVS'] = 1; + if($instrumentCode == 'CVS' && !empty($values['expiration_day'])) { + $day = !empty($values['expiration_day']) ? $values['expiration_day'] : self::EXPIRE_DAY; + $args['ExpireDate'] = date('Ymd',strtotime("+$day day")); + } + // $args['ReturnURL'] = url('spgateway/record/'.$vars['contributionID'], array('absolute' => true)); + // $args['Desc_1'] = ''; + // $args['Desc_2'] = ''; + // $args['Desc_3'] = ''; + // $args['Desc_4'] = ''; + + #ATM / CVS / BARCODE + $args['CustomerURL'] = $thankyouURL; + break; + case 'WebATM': + $args['WEBATM'] = 1; + $args['ReturnURL'] = $thankyouURL; + break; + case 'Credit': + $args['CREDIT'] = 1; + $args['ReturnURL'] = $thankyouURL; + break; + case 'GooglePay': + $args['ANDROIDPAY'] = 1; + $args['ReturnURL'] = $thankyouURL; + break; + } + + if($tsLocale == CRM_Core_Config::SYSTEM_LANG){ + $args['LangType'] = 'en'; + } + // Use hook_civicrm_alterPaymentProcessorParams + $mode = $this->_paymentProcessor['is_test'] ? 'test' : 'live'; + $paymentClass = CRM_Core_Payment::singleton($mode, $this->_paymentProcessor, CRM_Core_DAO::$_nullObject); + CRM_Utils_Hook::alterPaymentProcessorParams($paymentClass, $vars, $args); + } + else{ + $data = array( + 'MerchantID' => $this->_paymentProcessor['user_name'], + 'RespondType' => self::RESPONSE_TYPE, + 'TimeStamp' => time(), + 'Version' => self::RECUR_VERSION, + 'Amt' => $amount, + 'NotifyURL' => $notifyURL."&qfKey=".$vars['qfKey'], + 'PayerEmail' => $vars['email-5'], + 'LoginType' => '0', + 'MerOrderNo' => $vars['trxn_id'], + 'ProdDesc' => $itemDescription, + 'PeriodAmt' => $amount, + 'PeriodStartType' => 2, + 'ReturnURL' => $thankyouURL, + 'PaymentInfo' => 'N', + 'OrderInfo' => 'N', + ); + $period = strtoupper($vars['frequency_unit'][0]); + + if($vars['frequency_unit'] == 'month'){ + $frequency_interval = $vars['frequency_interval'] > 12 ? 12 : $vars['frequency_interval']; + $data['PeriodType'] = 'M'; + $data['PeriodPoint'] = date('d'); + } + elseif($vars['frequency_unit'] == 'week'){ + $frequency_interval = (7 * $vars['frequency_interval']) > 365 ? 365 : ($vars['frequency_interval'] * 7); + $data['PeriodType'] = 'W'; + } + elseif($vars['frequency_unit'] == 'year'){ + $frequency_interval = 1; + $data['PeriodType'] = 'Y'; + $data['PeriodPoint'] = date('md'); + } + if(empty($frequency_interval)){ + $frequency_interval = 1; + } + // $data['PeriodTimes'] = $frequency_interval; + if($vars['frequency_unit'] == 'year'){ + $data['PeriodTimes'] = empty($vars['installments']) ? 9 : $vars['installments']; + }else{ + $data['PeriodTimes'] = empty($vars['installments']) ? 99 : $vars['installments']; // support endless + } + if($tsLocale == CRM_Core_Config::SYSTEM_LANG){ + $data['LangType'] = 'en'; + } + // Use hook_civicrm_alterPaymentProcessorParams + $mode = $this->_paymentProcessor['is_test'] ? 'test' : 'live'; + $paymentClass = CRM_Core_Payment::singleton($mode, $this->_paymentProcessor, CRM_Core_DAO::$_nullObject); + CRM_Utils_Hook::alterPaymentProcessorParams($paymentClass, $vars, $data); + // Encrypt Recurring Request. + $str = http_build_query($data, '', '&'); + $strPost = CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt($str, $this->_paymentProcessor); + $args['PostData_'] = $strPost; + $args['MerchantID_'] = $this->_paymentProcessor['user_name']; + if ($this->_paymentProcessor['is_test']) { + $args['#url'] = self::TEST_DOMAIN.self::URL_RECUR; + } + else { + $args['#url'] = self::REAL_DOMAIN.self::URL_RECUR; + } + } + + + return $args ; + } + + + private function redirectForm($vars){ + header('Pragma: no-cache'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Expires: 0'); + + $output = ""; + + $js = 'document.forms.redirect.submit();'; + $output .= '
'; + foreach($vars as $k=>$p){ + if($k[0] != '#'){ + $output .= ''; + } + } + $output .= '
'; + return << + + + + + + {$output} + + + +EOT; + } + + public static function mobileCheckout($type, $post, $objects) { + $contribution = $objects['contribution']; + $merchantPaymentProcessor = $objects['payment_processor']; + + if($type = 'applepay') { + $email = new CRM_Core_DAO_Email(); + $email->contact_id = $contribution->contact_id; + $email->is_primary = true; + $email->find(TRUE); + + $token = urlencode(json_encode($post['token'])); + $is_test = $contribution->is_test; + + $params = array( + 'TimeStamp' => time(), + 'Version' => '1.0', + 'MerchantOrderNo' => CRM_Core_Payment_SPGATEWAY::generateTrxnId($is_test, $contribution->id), + 'Amt' => $contribution->total_amount, + 'ProdDesc' => $post['description'], + 'PayerEmail' => $email->email, + 'CardNo' => '', + 'Exp' => '', + 'CVC' => '', + 'APPLEPAY' => $token, + 'APPLEPAYTYPE' => '02', + ); + CRM_Core_Error::debug('applepay_transact_curl_params_before_encrypt', $params); + + $data = CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt(http_build_query($params), get_object_vars($merchantPaymentProcessor)); + + $data = array( + 'MerchantID_' => $merchantPaymentProcessor->user_name, + 'PostData_' => $data, + 'Pos_' => 'JSON', + ); + if($contribution->is_test){ + $url = CRM_Core_Payment_SPGATEWAY::TEST_DOMAIN.CRM_Core_Payment_SPGATEWAY::URL_CREDITBG; + }else{ + $url = CRM_Core_Payment_SPGATEWAY::REAL_DOMAIN.CRM_Core_Payment_SPGATEWAY::URL_CREDITBG; + } + + CRM_Core_Error::debug('applepay_transact_curl_data_after_encrypt', $data); + + $ch = curl_init($url); + $opt = array(); + $opt[CURLOPT_RETURNTRANSFER] = TRUE; + $opt[CURLOPT_POST] = TRUE; + $opt[CURLOPT_POSTFIELDS] = $data; + $opt[CURLOPT_SSL_VERIFYPEER] = FALSE; + curl_setopt_array($ch, $opt); + + $result = curl_exec($ch); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($result === FALSE) { + $errno = curl_errno($ch); + $err = curl_error($ch); + $curlError = array($errno => $err); + } + else{ + $curlError = array(); + } + curl_close($ch); + CRM_Core_Error::debug('applepay_transact_curl_error', $curlError); + + $result = json_decode($result); + CRM_Core_Payment_SPGATEWAYAPI::writeRecord($contribution->id, get_object_vars($result)); + $return = array(); + if($result->Status == 'SUCCESS'){ + $return['is_success'] = true; + } + $return['message'] = $result->Message; + } + return $return; + } - /* - * $params = array( - * 'contribution_recur_id => Positive, - * 'contribution_status_id' => Positive(7 => suspend, 3 => terminate, 5 => restart), - * 'amount' => Positive, - * 'frequency_unit' => String('year', 'month') - * 'cycle_day' => Positive(1 - 31, 101 - 1231) - * 'end_date' => Date - * ) - */ function doUpdateRecur($params, $debug = FALSE) { if ($debug) { CRM_Core_Error::debug('SPGATEWAY doUpdateRecur $params', $params); @@ -243,10 +626,7 @@ function doUpdateRecur($params, $debug = FALSE) { if ($this->_paymentProcessor['url_recur'] != 1) { return $params; } - if (module_load_include('inc', 'civicrm_spgateway', 'civicrm_spgateway.api') === FALSE) { - CRM_Core_Error::fatal('Module civicrm_spgateway doesn\'t exists.'); - } - else if (empty($params['contribution_recur_id'])) { + if (empty($params['contribution_recur_id'])) { CRM_Core_Error::fatal('Missing contribution recur ID in params'); } else { @@ -283,9 +663,9 @@ function doUpdateRecur($params, $debug = FALSE) { if (!empty($params['contribution_status_id'])) { $apiConstructParams['apiType'] = 'alter-status'; - $spgatewayAPI = new spgateway_spgateway_api($apiConstructParams); + $spgatewayAPI = new CRM_Core_Payment_SPGATEWAYAPI($apiConstructParams); $newStatusId = $params['contribution_status_id']; - + /* * $requestParams = array( * 'AlterStatus' => Positive(7 => suspend, 3 => terminate, 5 => restart), @@ -330,7 +710,7 @@ function doUpdateRecur($params, $debug = FALSE) { // Send alter other property API. $apiConstructParams['apiType'] = 'alter-amt'; - $spgatewayAPI = new spgateway_spgateway_api($apiConstructParams); + $spgatewayAPI = new CRM_Core_Payment_SPGATEWAYAPI($apiConstructParams); $isChangeRecur = FALSE; $requestParams = array( 'Version' => self::$_recurEditAPIVersion, @@ -430,59 +810,6 @@ function cancelRecuringMessage($recurID){ } } - /** - * return array( - * // All instrument: - * 'status' => contribuion_status - * 'msg' => return message - * - * // Not Credit Card: - * 'payment_instrument' => civicrm_spgateway_notify_display() return value - * ) - */ - function doGetResultFromIPNNotify($contributionId, $submitValues = array()) { - // First, check if it is redirect payment. - $instruments = CRM_Contribute_PseudoConstant::paymentInstrument('Name'); - $cDao = new CRM_Contribute_DAO_Contribution(); - $cDao->id = $contributionId; - $cDao->fetch(TRUE); - if (strstr($instruments[$cDao->payment_instrument_id], 'Credit')) { - // If contribution status id == 2, wait 3 second for IPN trigger - if ($cDao->contribution_status_id == 2) { - sleep(3); - $contribution_status_id = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'contribution_status_id'); - if ($contribution_status_id == 2) { - $ids = CRM_Contribute_BAO_Contribution::buildIds($contributionId); - $query = CRM_Contribute_BAO_Contribution::makeNotifyUrl($ids, NULL, TRUE); - parse_str($query, $get); - $result = civicrm_spgateway_ipn('Credit', $submitValues, $get, FALSE); - if(strstr($result, 'OK')){ - $status = 1; - } - else{ - $status = 2; - } - } - } - else { - $status = $cDao->contribution_status_id; - if (!empty($submitValues['JSONData'])) { - $return_params = _civicrm_spgateway_post_decode($submitValues['JSONData']); - } - if(!empty($submitValues['Period']) && empty($return_params)){ - $payment_processors = CRM_Core_BAO_PaymentProcessor::getPayment($cDao->payment_processor_id, $cDao->is_test?'test':'live'); - $return_params = _civicrm_spgateway_post_decode(_civicrm_spgateway_recur_decrypt($submitValues['Period'], $payment_processors)); - } - $msg = _civicrm_spgateway_error_msg($return_params['RtnCode']); - } - } - else { - - } - - } - - /** * Function called from contributionRecur page to show tappay detail information * @@ -534,7 +861,7 @@ public static function doRecurUpdate($id, $idType = 'contribution', $form = NULL } } - $result = civicrm_spgateway_single_check($trxn_id, TRUE); + $result = self::recurSyncTransaction($trxn_id, TRUE); $session = CRM_Core_Session::singleton(); if (!empty($result)) { if ($isAddedNewContribution) { @@ -565,7 +892,7 @@ public static function doRecurUpdate($id, $idType = 'contribution', $form = NULL } } - static function doSingleQueryRecord($contributionId = NULL) { + public static function doSingleQueryRecord($contributionId = NULL, $order = NULL) { $get = $_GET; unset($get['q']); if (!is_numeric($contributionId) || empty($contributionId)) { @@ -582,68 +909,65 @@ static function doSingleQueryRecord($contributionId = NULL) { $resultMessage = ts("The contribution with transaction ID: %1 can't find from Newebpay API.", array(1 => $cid)); } else { - if (module_load_include('inc', 'civicrm_spgateway', 'civicrm_spgateway.checkout') === FALSE) { - $resultMessage = ts('Module %1 doesn\'t exists.', array(1 => 'civicrm_spgateway')); + if (!empty($order)) { + // this is for ci testing or something we already had response + // should be object or associated array + self::syncTransaction($trxnId, $order); } else { - if (!function_exists('civicrm_spgateway_single_contribution_sync')) { - $resultMessage = ts("Sync single contribution function doesn't exist."); - } - else { - civicrm_spgateway_single_contribution_sync($trxnId); - $resultMessage = ts("Synchronizing to %1 server success.", array(1 => ts("NewebPay"))); - $updatedDAO = new CRM_Contribute_DAO_Contribution(); - $updatedDAO->id = $cid; - $updatedDAO->find(TRUE); - $diffContribution = array(); - if ($updatedDAO->contribution_status_id != $origDAO->contribution_status_id) { - $status = CRM_Contribute_PseudoConstant::contributionStatus(); - $diffContribution[ts('Contribution Status')] = array($status[$origDAO->contribution_status_id], $status[$updatedDAO->contribution_status_id]); - - // Check it will send Email. - $components = CRM_Contribute_BAO_Contribution::getComponentDetails(array($cid)); - $contributeComponent = $components[$cid]; - $componentName = $contributeComponent['component']; - $pageId = $contributeComponent['page_id']; - if ($componentName == 'contribute' && !empty($pageId)) { - $pageParams = array(1 => array( $pageId, 'Positive')); - $isEmailReceipt = CRM_Core_DAO::singleValueQuery("SELECT is_email_receipt FROM civicrm_contribution_page WHERE id = %1", $pageParams); - if ($isEmailReceipt) { - $diffContribution[] = ts('A notification email has been sent to the supporter.'); - } - } + self::syncTransaction($trxnId); + } + $resultMessage = ts("Synchronizing to %1 server success.", array(1 => ts("NewebPay"))); + $updatedDAO = new CRM_Contribute_DAO_Contribution(); + $updatedDAO->id = $cid; + $updatedDAO->find(TRUE); + $diffContribution = array(); + if ($updatedDAO->contribution_status_id != $origDAO->contribution_status_id) { + $status = CRM_Contribute_PseudoConstant::contributionStatus(); + $diffContribution[ts('Contribution Status')] = array($status[$origDAO->contribution_status_id], $status[$updatedDAO->contribution_status_id]); - // Check if the SMS is sent. - $activityType = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name', TRUE); - $activitySMSParams = array( - 'source_record_id' => $cid, - 'activity_type_id' => CRM_Utils_Array::key('Contribution SMS', $activityType), - ); - $smsActivity = new CRM_Activity_DAO_Activity(); - $smsActivity->copyValues($activitySMSParams); - if ($smsActivity->find(TRUE)) { - $diffContribution[] = ts('SMS Sent'); - } - } - if ($updatedDAO->receive_date != $origDAO->receive_date) { - $diffContribution[ts('Received Date')] = array($origDAO->receive_date, $updatedDAO->receive_date); - } - if ($updatedDAO->cancel_date != $origDAO->cancel_date) { - $diffContribution[ts('Cancel Date')] = array($origDAO->cancel_date, $updatedDAO->cancel_date); - } - if ($updatedDAO->cancel_reason != $origDAO->cancel_reason) { - $diffContribution[ts('Cancel Reason')] = array($origDAO->cancel_reason, $updatedDAO->cancel_reason); - } - if ($updatedDAO->receipt_id != $origDAO->receipt_id) { - $diffContribution[ts('Receipt ID')] = array($origDAO->receipt_id, $updatedDAO->receipt_id); - } - if ($updatedDAO->receipt_date != $origDAO->receipt_date) { - $diffContribution[ts('Receipt Date')] = array($origDAO->receipt_date, $updatedDAO->receipt_date); - } - if (empty($diffContribution)) { - $diffContribution[] = ts("There are no any change."); + // Check it will send Email. + $components = CRM_Contribute_BAO_Contribution::getComponentDetails(array($cid)); + $contributeComponent = $components[$cid]; + $componentName = $contributeComponent['component']; + $pageId = $contributeComponent['page_id']; + if ($componentName == 'contribute' && !empty($pageId)) { + $pageParams = array(1 => array( $pageId, 'Positive')); + $isEmailReceipt = CRM_Core_DAO::singleValueQuery("SELECT is_email_receipt FROM civicrm_contribution_page WHERE id = %1", $pageParams); + if ($isEmailReceipt) { + $diffContribution[] = ts('A notification email has been sent to the supporter.'); } } + + // Check if the SMS is sent. + $activityType = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name', TRUE); + $activitySMSParams = array( + 'source_record_id' => $cid, + 'activity_type_id' => CRM_Utils_Array::key('Contribution SMS', $activityType), + ); + $smsActivity = new CRM_Activity_DAO_Activity(); + $smsActivity->copyValues($activitySMSParams); + if ($smsActivity->find(TRUE)) { + $diffContribution[] = ts('SMS Sent'); + } + } + if ($updatedDAO->receive_date != $origDAO->receive_date) { + $diffContribution[ts('Received Date')] = array($origDAO->receive_date, $updatedDAO->receive_date); + } + if ($updatedDAO->cancel_date != $origDAO->cancel_date) { + $diffContribution[ts('Cancel Date')] = array($origDAO->cancel_date, $updatedDAO->cancel_date); + } + if ($updatedDAO->cancel_reason != $origDAO->cancel_reason) { + $diffContribution[ts('Cancel Reason')] = array($origDAO->cancel_reason, $updatedDAO->cancel_reason); + } + if ($updatedDAO->receipt_id != $origDAO->receipt_id) { + $diffContribution[ts('Receipt ID')] = array($origDAO->receipt_id, $updatedDAO->receipt_id); + } + if ($updatedDAO->receipt_date != $origDAO->receipt_date) { + $diffContribution[ts('Receipt Date')] = array($origDAO->receipt_date, $updatedDAO->receipt_date); + } + if (empty($diffContribution)) { + $diffContribution[] = ts("There are no any change."); } } // Redirect to contribution view page. @@ -661,11 +985,13 @@ static function doSingleQueryRecord($contributionId = NULL) { } $resultMessage.=""; } - CRM_Core_Session::setStatus($resultMessage); - CRM_Utils_System::redirect($redirect); + if (!isset($order)) { + CRM_Core_Session::setStatus($resultMessage); + CRM_Utils_System::redirect($redirect); + } } - static function getSyncDataUrl($contributionId, &$form = NULL) { + public static function getSyncDataUrl($contributionId, &$form = NULL) { $get = $_GET; unset($get['q']); $query = http_build_query($get); @@ -698,5 +1024,560 @@ static function getSyncDataUrl($contributionId, &$form = NULL) { return $sync_url; } -} + /** + * The IPN warpping + * + * @param array $arguments Router will pass array into this function + * @param array $post Simulate POST data + * @param array $get Simulate GET data + * @param bool $print print result + * @return void + */ + public static function doIPN($arguments, $post = NULL, $get = NULL, $print = TRUE) { + $post = !empty($post) ? $post : $_POST; + $get = !empty($get) ? $get : $_GET; + if (!empty($arguments)) { + if (is_array($arguments)) { + $instrument = end($arguments); + } + elseif (is_string($arguments)){ + $instrument = $arguments; + } + else { + CRM_Core_Error::debug_log_message("Spgateway: IPN Missing require argument."); + CRM_Utils_System::civiExit(); + } + } + + // detect variables + if(empty($post)){ + CRM_Core_Error::debug_log_message("Spgateway: Could not find POST data from payment server."); + CRM_Utils_System::civiExit(); + } + else{ + // validate some post + if (!empty($post['JSONData']) || !empty($post['Period']) || !empty($post['Result'])) { + $ipn = new CRM_Core_Payment_SPGATEWAYIPN($post, $get); + $result = $ipn->main($instrument); + if(is_string($result) && $print){ + echo $result; + } + else{ + return $result; + } + } + else { + CRM_Core_Error::debug_log_message("Spgateway: Invlid POST data."); + CRM_Core_Error::debug_var("spgateway_ipn_post", $post); + } + } + CRM_Utils_System::civiExit(); + } + + /** + * Migrate from _civicrm_spgateway_instrument + * + * @param string $type + * @return void + */ + public static function instruments($type = 'normal'){ + $i = array( + 'Credit Card' => array('label' => ts('Credit Card'), 'desc' => '', 'code' => 'Credit'), + 'ATM' => array('label' => ts('ATM Transfer'), 'desc' => '', 'code' => 'ATM'), + 'Web ATM' => array('label' => ts('Web ATM'), 'desc' => '', 'code' => 'WebATM'), + 'Convenient Store' => array('label' => ts('Convenient Store Barcode'), 'desc'=>'', 'code' => 'BARCODE'), + 'Convenient Store (Code)' => array('label'=> ts('Convenient Store (Code)'),'desc' => '', 'code' => 'CVS'), + ); + if($type == 'form_name'){ + foreach($i as $name => $data){ + $form_name = preg_replace('/[^0-9a-z]+/i', '_', strtolower($name)); + $instrument[$form_name] = $data; + } + return $instrument; + } + elseif($type == 'code'){ + foreach($i as $name => $data){ + $instrument[$name] = $data['code']; + } + return $instrument; + } + else{ + return $i; + } + } + + /** + * Migrate from _civicrm_spgateway_trxn_id + * + * @param int $is_test + * @param int $id + * @return string + */ + public static function generateTrxnId($is_test, $id){ + if($is_test){ + $trxnId = 'test' . substr(str_replace(array('.','-'), '', $_SERVER['HTTP_HOST']), 0, 3) . $id. 'T'. mt_rand(100, 999); + return $trxnId; + } + return (string) $id; + } + + /** + * Migrate from civicrm_spgateway_single_contribution_sync + * + * Sync non-recurring transaction by specific trxn id + * + * @param int $inputTrxnId + * @return bool|object + * @param string $order + */ + public static function syncTransaction($inputTrxnId, $order = NULL) { + $paymentProcessorId = 0; + + // Check contribution is exists + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->trxn_id = $inputTrxnId; + if ($contribution->find(TRUE)) { + $paymentProcessorId = $contribution->payment_processor_id; + if ($contribution->contribution_status_id == 1) { + $message = ts('There are no any change.'); + return $message; + } + } + // we can't support single contribution check because lake of payment processor id #TODO - logic to get payment processor id + if (!empty($paymentProcessorId)) { + $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($paymentProcessorId, $contribution->is_test ? 'test': 'live'); + + if (strstr($paymentProcessor['payment_processor_type'], 'SPGATEWAY') && !empty($paymentProcessor['user_name'])) { + if ($order) { + // this is for ci testing or something we already had response + // should be object or associated array + $result = $order; + } + else { + $amount = $contribution->total_amount; + if ($contribution->contribution_recur_id && !strstr($inputTrxnId, '_')) { + $trxnId = $inputTrxnId.'_1'; + $recurring_first_contribution = TRUE; + } + else { + $trxnId = $inputTrxnId; + } + $data = array( + 'Amt' => floor($amount), + 'MerchantID' => $paymentProcessor['user_name'], + 'MerchantOrderNo' => $trxnId, + 'RespondType' => self::RESPONSE_TYPE, + 'TimeStamp' => CRM_REQUEST_TIME, + 'Version' => self::QUERY_VERSION, + ); + $args = array('IV','Amt','MerchantID','MerchantOrderNo', 'Key'); + CRM_Core_Payment_SPGATEWAYAPI::encode($data, $paymentProcessor, $args); + $urlApi = $contribution->is_test ? self::TEST_DOMAIN.self::URL_API : self::REAL_DOMAIN.self::URL_API; + $result = CRM_Core_Payment_SPGATEWAYAPI::sendRequest($urlApi, $data); + } + + // Online contribution + // Only trigger if there are pay time in result; + if (!empty($result) && $result->Status == 'SUCCESS' && $result->Result->TradeStatus !== '0') { + // complex part to simulate spgateway ipn + $ipnGet = $ipnPost = array(); + + // prepare post, complex logic because recurring have different variable names + $ipnResult = clone $result; + if ($result->Result->TradeStatus != 1) { + $ipnResult->Status =$result->Result->RespondCode; + } + $ipnResult->Message = $result->Result->RespondMsg; + // Pass CheckCode. + unset($ipnResult->Result->CheckCode); + $ipnPost = (array) $ipnResult; + + if ($contribution->contribution_recur_id) { + $ipnResult->Result->AuthAmt = $result->Result->Amt; + unset($ipnResult->Result->Amt); + $ipnResult->Result->OrderNo = $result->Result->MerchantOrderNo; + list($first_id, $period_times) = explode('_', $result->Result->MerchantOrderNo); + if(!empty($period_times) && $period_times != 1){ + $ipnResult->Result->AlreadyTimes = $period_times; + } + $ipnResult->Result->MerchantOrderNo = $first_id; + $ipnResult = json_encode($ipnResult); + $ipnPost = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt($ipnResult, $paymentProcessor)); + } + + // prepare get + $ids = CRM_Contribute_BAO_Contribution::buildIds($contribution->id); + $query = CRM_Contribute_BAO_Contribution::makeNotifyUrl($ids, NULL, TRUE); + parse_str($query, $ipnGet); + + // create recurring record + $result->_post = $ipnPost; + $result->_get = $ipnGet; + $result->_response = self::doIPN(array('spgateway', 'ipn', 'Credit'), $result->_post, $result->_get, FALSE); + + if ($recurring_first_contribution) { + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->trxn_id = $inputTrxnId; + if ($contribution->find(TRUE) && strstr($trxnId, '_1')) { + // The case first contribution trxn_id not append '_1' in the end. + CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $contribution->id, 'trxn_id', $trxnId); + } + } + return $result; + } + } + } + return FALSE; + } + + /** + * Always trying to fetch next trxn_id which not appear in CRM + */ + /** + * Migrate from civicrm_spgateway_recur_check + * + * Trying to get next transaction by given recurring id + * + * @param int $recurId + * @return void + */ + public static function recurSync($recurId) { + $query = "SELECT trxn_id, CAST(REGEXP_REPLACE(trxn_id, '^[0-9_r]+_([0-9]+)$', '\\\\1') as UNSIGNED) as number FROM civicrm_contribution WHERE contribution_recur_id = %1 AND CAST(trxn_id as UNSIGNED) < 900000000 ORDER BY number DESC"; + $result = CRM_Core_DAO::executeQuery($query, array(1 => array($recurId, 'Integer'))); + $result->fetch(); + if(!empty($result->N)){ + // when recurring trxn_id have underline, eg oooo_1 + if (strstr($result->trxn_id, '_')) { + list($parentTrxnId, $recurringInstallment, $oldRecurInstallment) = explode('_', $result->trxn_id); + if ($parentTrxnId == 'r' && is_numeric($oldRecurInstallment)) { + // for old recurring. trxn_id like 'r_12_3', $parentTrxnId = 'r', $recurringInstallment = 12, $oldRecurInstallment = 3 + $oldRecurInstallment++; + self::recurSyncTransaction($parentTrxnId.'_'.$recurringInstallment.'_'.$oldRecurInstallment, $createContribution = TRUE); + } + elseif (is_numeric($recurringInstallment)) { + // for current recurring, for trxn_id like 123_4, $parentTrxnId = 123, $recurringInstallment = 4 + $recurringInstallment++; + self::recurSyncTransaction($parentTrxnId.'_'.$recurringInstallment, $createContribution = TRUE); + } + } + // when first recurring trxn_id record without underline + else { + $parentTrxnId= $result->trxn_id; + $installment = 2; + self::recurSyncTransaction($parentTrxnId, TRUE); + } + } + } + + /** + * Migrate from civicrm_spgateway_single_check + * + * Create recurring transacation by specific trxn id + * Base on spgateway / neweb response + * + * @param string $trxnId + * @param bool $createContribution + * @param string $order + * @return object|bool + */ + public static function recurSyncTransaction($trxnId, $createContribution = FALSE, $order = NULL) { + $parentTrxnId = 0; + $paymentProcessorId = 0; + if (strstr($trxnId, '_')) { + list($recurId, $installment, $oldInstallment) = explode('_', $trxnId); + if ($recurId == 'r' && !empty($oldInstallment)) { + // Old newebpay recurring, format: r_123_4 + $parentTrxnId = $recurId.'_'.$installment; + } + else { + // Current spgateway recurring, format: 1234_5 + $parentTrxnId = $recurId; + } + } + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->trxn_id = $trxnId; + if ($contribution->find(TRUE)) { + $paymentProcessorId = $contribution->payment_processor_id; + if ($contribution->contribution_status_id == 1) { + $createContribution = FALSE; // Found, And contribution is already success. + } + elseif (empty($parentTrxnId) && $createContribution) { + // First recurring or single contribution. + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->id = $trxnId; + if ($contribution->find(TRUE)) { + $paymentProcessorId = $contribution->payment_processor_id; + if (!empty($contribution->contribution_recur_id)) { + // First recurring contribution + $trxnId = $trxnId.'_1'; + } + } + } + } + elseif($createContribution) { + // recurring contribution + if ($parentTrxnId) { + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->trxn_id = $parentTrxnId.'_1'; + if ($contribution->find(TRUE)) { + $paymentProcessorId = $contribution->payment_processor_id; + } + else { + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->trxn_id = $parentTrxnId; + if ($contribution->find(TRUE)) { + $paymentProcessorId = $contribution->payment_processor_id; + } + } + } + } + + // we can't support single contribution check because lake of payment processor id #TODO - logic to get payment processor id + if (!empty($paymentProcessorId)) { + $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($paymentProcessorId, $contribution->is_test ? 'test': 'live'); + + if (!empty($paymentProcessor['user_name'])) { + if ($order) { + // this is for ci testing or something we already had response + // should be object or associated array + $result = $order; + } + else { + $amount = CRM_Core_DAO::singleValueQuery('SELECT amount FROM civicrm_contribution_recur WHERE id = %1', array(1 => array($contribution->contribution_recur_id, 'Positive'))); + $data = array( + 'Amt' => floor($amount), + 'MerchantID' => $paymentProcessor['user_name'], + 'MerchantOrderNo' => $trxnId, + 'RespondType' => self::RESPONSE_TYPE, + 'TimeStamp' => CRM_REQUEST_TIME, + 'Version' => self::QUERY_VERSION, + ); + $args = array('IV','Amt','MerchantID','MerchantOrderNo', 'Key'); + CRM_Core_Payment_SPGATEWAYAPI::encode($data, $paymentProcessor, $args); + $urlApi = $contribution->is_test ? self::TEST_DOMAIN.self::URL_API : self::REAL_DOMAIN.self::URL_API; + $result = CRM_Core_Payment_SPGATEWAYAPI::sendRequest($urlApi, $data); + } + + // Online contribution + if (!empty($result) && $result->Status == 'SUCCESS') { + if ($createContribution && $contribution->id) { + // complex part to simulate spgateway ipn + $ipnGet = $ipnPost = array(); + + // prepare post, complex logic because recurring have different variable names + $ipnResult = clone $result; + if ($result->Result->TradeStatus != 1) { + $ipnResult->Status =$result->Result->RespondCode; + } + $ipnResult->Message = $result->Result->RespondMsg; + + $ipnResult->Result->AuthAmt = $result->Result->Amt; + unset($ipnResult->Result->Amt); + unset($ipnResult->Result->CheckCode); + $ipnResult->Result->OrderNo = $result->Result->MerchantOrderNo; + list($first_id, $period_times) = explode('_', $result->Result->MerchantOrderNo); + if(!empty($period_times) && $period_times != 1){ + $ipnResult->Result->AlreadyTimes = $period_times; + } + $ipnResult->Result->MerchantOrderNo = $first_id; + $ipnResult = json_encode($ipnResult); + $ipnPost = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt($ipnResult, $paymentProcessor)); + + // prepare get + $ids = CRM_Contribute_BAO_Contribution::buildIds($contribution->id); + $query = CRM_Contribute_BAO_Contribution::makeNotifyUrl($ids, NULL, $return_query = TRUE); + parse_str($query, $ipnGet); + + // create recurring record + $result->_post = $ipnPost; + $result->_get = $ipnGet; + $result->_response = self::doIPN(array('spgateway', 'ipn', 'Credit'), $ipnPost, $ipnGet, FALSE); + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->trxn_id = $parentTrxnId; + if ($contribution->find(TRUE) && strstr($trxnId, '_1')) { + // The case first contribution trxn_id not append '_1' in the end. + CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $contribution->id, 'trxn_id', $trxnId); + } + return $result; + } + else { + return $result; + } + } + } + } + return FALSE; + } + + /** + * Using weblog post data to sync + * + * @param string $processOnlyDate string that format YYYYmmddHH indicate only process date in that hour + * @param array $lines custom provided lines for test o process + * @return void + */ + public static function syncTransactionWebLog($filterDatetime = '', $lines = NULL) { + $paymentProcessors = array(); + if (isset($lines) && is_array($lines)) { + $logLines = $lines; + } + else { + $logLines = array(); + $logFile = CRM_Utils_System::cmsRootPath().'/'.CRM_Core_Config::singleton()->webLogDir.'/ipn_post.log'; + $logContent = ''; + if (strpos($logFile, '/') === 0 && is_file($logFile)) { + $logContent = file_get_contents($logFile); + $logLines = explode("\n", $logContent); + } + } + + if (!empty($logLines)) { + $ordersByMerchant = array(); + foreach ($logLines as $idx => $logLine) { + $getParams = $ipnResult = $postParams = array(); + $logLine = trim($logLine); + if (empty($logLine)) { + continue; + } + // separate by space + preg_match('/^([^ ]+)\s\[([^\s]+)\]\s([^\s]+)\s(.*)$/', $logLine, $logMatches); + if (count($logMatches) >= 4) { + $isoDate = $logMatches[2]; + // skip ipn when filter + if ($filterDatetime && strpos(date('YmdH', strtotime($isoDate)), $filterDatetime) === FALSE) { + continue; + } + + $getString = $logMatches[3]; + $postString = isset($logMatches[4]) ? $logMatches[4] : ''; + $postString = preg_replace_callback('/\\\\x([0-9a-fA-F]{2})/', function ($strMatches) { + return chr(hexdec($strMatches[1])); + }, $postString); + + $parsedUrl = parse_url($getString); + parse_str($parsedUrl['query'], $getParams); + + // analysis POST + if (!empty($postString)) { + if (strpos($postString, 'JSONData=') === 0) { + $postString = substr($postString, 9); + $postParams = json_decode($postString, true); + if (!empty($postParams['Result'])) { + $ipnResult = json_decode($postParams['Result'], TRUE); + if (!empty($ipnResult['MerchantID'])) { + $ordersByMerchant[$ipnResult['MerchantID']][$idx] = array( + 'recurring' => FALSE, + 'contribution_id' => !empty($getParams['cid']) ? $getParams['cid'] : '', + 'contact_id' => !empty($getParams['contact_id']) ? $getParams['contact_id'] : '', + 'success' => (isset($postParams['Status']) && $postParams['Status'] === 'SUCCESS') ? TRUE : FALSE, + 'total_amount' => isset($ipnResult['Amt']) ? (float)$ipnResult['Amt'] : 0, + 'trxn_id' => isset($ipnResult['MerchantOrderNo']) ? $ipnResult['MerchantOrderNo'] : '', + 'receive_date' => isset($ipnResult['PayTime']) ? date('c', strtotime($ipnResult['PayTime'])) : '', + 'ipn_date' => $isoDate, + ); + } + } + } + elseif (strpos($postString, 'Content-Disposition: form-data') !== false) { + $rawPostData = preg_replace('/\r\n--------------------------\w+--\r\n$/', '', $postString); + $postParts = preg_split('/\r\n--------------------------\w+\r\n/', $rawPostData); + $postParams = array(); + foreach ($postParts as $part) { + if (preg_match('/Content-Disposition: form-data; name="(.+?)"\r\n\r\n(.*)/s', $part, $strMatches)) { + $key = $strMatches[1]; + $value = $strMatches[2]; + $postParams[$key] = $value; + } + } + if (!empty($postParams['Period'])) { + // decode + if(is_numeric($getParams['cid'])) { + $dao = CRM_Core_DAO::executeQuery("SELECT payment_processor_id, is_test FROM civicrm_contribution WHERE id = %1", array( + 1 => array($getParams['cid'], 'Integer'), + )); + $dao->fetch(); + if (!empty($paymentProcessors[$dao->payment_processor_id])) { + $paymentProcessor = $paymentProcessors[$dao->payment_processor_id]; + } + elseif (!empty($dao->payment_processor_id)) { + $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($dao->payment_processor_id, $dao->is_test ? 'test' : 'live'); + } + $postDecode = CRM_Core_Payment_SPGATEWAYAPI::recurDecrypt($postParams['Period'], $paymentProcessor); + if (!empty($postDecode) && json_decode($postDecode)) { + $postParams = json_decode($postDecode, TRUE); + $ipnResult = $postParams['Result']; + if (!empty($ipnResult['OrderNo'])) { + $orderNumber = $ipnResult['OrderNo']; + $firstRecurring = FALSE; + } + else { + $orderNumber = $ipnResult['MerchantOrderNo']; + $firstRecurring = TRUE; + } + $amount = isset($ipnResult['AuthAmt']) ? $ipnResult['AuthAmt'] : $ipnResult['PeriodAmt']; + $ordersByMerchant[$ipnResult['MerchantID']][$idx] = array( + 'recurring' => TRUE, + 'first_recurring' => $firstRecurring, + 'contribution_id' => !empty($getParams['cid']) ? $getParams['cid'] : '', + 'contact_id' => !empty($getParams['contact_id']) ? $getParams['contact_id'] : '', + 'success' => (isset($postParams['Status']) && $postParams['Status'] === 'SUCCESS') ? TRUE : FALSE, + 'total_amount' => $amount, + 'trxn_id' => $orderNumber, + 'receive_date' => isset($ipnResult['AuthTime']) ? date('c', strtotime($ipnResult['AuthTime'])) : '', + 'ipn_date' => $isoDate, + ); + } + } + } + } + } + } + } + // process complete, start to call sync when necessery + // only trigger sync when contribution status is pending + if (!empty($ordersByMerchant)) { + foreach($ordersByMerchant as $merchantId => $orders) { + foreach($orders as $idx => $order) { + if (!empty($order['trxn_id'])) { + if ($order['first_recurring']) { + $current_status_id = CRM_Core_DAO::singleValueQuery("SELECT contribution_status_id FROM civicrm_contribution WHERE trxn_id IN (%1, %2)", array( + 1 => array($order['trxn_id'], 'String'), + 2 => array($order['trxn_id'].'_1', 'String'), + )); + } + else { + $current_status_id = CRM_Core_DAO::singleValueQuery("SELECT contribution_status_id FROM civicrm_contribution WHERE trxn_id = %1", array( + 1 => array($order['trxn_id'], 'String'), + )); + } + if ($order['recurring']) { + if (empty($current_status_id)) { + CRM_Core_Error::debug_log_message("spgateway: weblog sync recur create for trxn_id {$order['trxn_id']}"); + self::recurSyncTransaction($order['trxn_id'], TRUE); + } + elseif($current_status_id == 2) { + CRM_Core_Error::debug_log_message("spgateway: weblog sync recur status for trxn_id {$order['trxn_id']}"); + if ($order['first_recurring']) { + $ids = explode('_', $order['trxn_id']); + self::syncTransaction($ids[0]); + } + else { + self::syncTransaction($order['trxn_id']); + } + } + } + else { + if ($current_status_id == 2) { + // only sync status + CRM_Core_Error::debug_log_message("spgateway: weblog sync non-recur status for trxn_id {$order['trxn_id']}"); + self::syncTransaction($order['trxn_id']); + } + } + } + } + } + } + } + } +} diff --git a/CRM/Core/Payment/SPGATEWAYAPI.php b/CRM/Core/Payment/SPGATEWAYAPI.php new file mode 100644 index 000000000..eb4a8b12e --- /dev/null +++ b/CRM/Core/Payment/SPGATEWAYAPI.php @@ -0,0 +1,678 @@ + '/MPG/period/AlterStatus', + 'alter-amt' => '/MPG/period/AlterAmt', + ); + public static $_alterStatus = array( + 'suspend', // Paused + 'terminate', // Stop + 'restart', // Only used in paused recur. + ); + + // Used for request result + public $_apiURL; + public $_request; + public $_response; + public $_success; + public $_curlResult; + + /** + * Constructor + * + * @param array $apiParams + * apiType + * paymentProcessorId + * paymentProcessor + * isTest + */ + function __construct($apiParams) { + if (!empty($apiParams['paymentProcessor'])) { + $this->_paymentProcessor = $apiParams['paymentProcessor']; + } + else if (!empty($apiParams['paymentProcessorId'])) { + $mode = $apiParams['isTest'] ? '' : 'test'; + $this->_paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($apiParams['paymentProcessorId'], $mode); + } + else { + CRM_Core_Error::fatal('Missing payment processor or payment processor ID'); + } + if ($apiParams['isTest']) { + $this->_urlDomain = CRM_Core_Payment_SPGATEWAY::TEST_DOMAIN; + } + else { + $this->_urlDomain = CRM_Core_Payment_SPGATEWAY::REAL_DOMAIN; + } + $this->_isTest = $apiParams['isTest']; + $this->_paymentProcessorId = $this->_paymentProcessor['id']; + $this->_apiType = $apiParams['apiType']; + } + + /** + * Validate and send request to spgateway / neweb + * + * @param array $params + * @return void + */ + public function request($params) { + $allowedFields = self::getRequestFields($this->_apiType); + $post = array(); + foreach ($params as $name => $value) { + if (!in_array($name, $allowedFields)) { + continue; + } + else { + $post[$name] = $value; + } + } + if (empty($post['RespondType'])) { + $post['RespondType'] = CRM_Core_Payment_SPGATEWAY::RESPONSE_TYPE; + } + if (empty($post['Version'])) { + $post['Version'] = CRM_Core_Payment_SPGATEWAY::RECUR_VERSION; + } + if (empty($post['TimeStamp'])) { + $post['TimeStamp'] = time(); + } + + $requiredFields = self::getRequestFields($this->_apiType, TRUE); + foreach ($requiredFields as $required) { + if(empty($post[$required])){ + $missingRequired[] = $required; + } + } + if(!empty($missingRequired)) { + CRM_Core_Error::fatal('Required parameters missing: '.implode(',', $missingRequired)); + } + + if (!empty($post['PeriodType']) xor !empty($post['PeriodPoint'])) { + CRM_Core_Error::fatal('PeriodType and PeriodPoint must exist at same time.'); + } + if (!empty($post['PeriodType'])) { + if ($post['PeriodType'] == 'Y' && !preg_match('/\d{4}/', $post['PeriodPoint'])) { + CRM_Core_Error::fatal('PeriodPoint format should be MMDD when PeriodType is "Y".'); + } + if ($post['PeriodType'] == 'M' && !preg_match('/\d{2}/', $post['PeriodPoint'])) { + CRM_Core_Error::fatal('PeriodPoint format should be DD when PeriodType is "M".'); + } + } + + $this->_request = $post; + $this->_apiURL = $this->_urlDomain.self::$_apiTypes[$this->_apiType]; + $result = $this->_curlResult = $this->_request(); + CRM_Core_Error::debug('Spgateway_RecurChangeAPI_request', $this->_request); + if ($result['status'] && !empty($this->_response)) { + + CRM_Core_Error::debug('Spgateway_RecurChangeAPI_response', $this->_response); + // Curl Correct and have response + + if ($this->_response->Status == 'SUCCESS') { + // Format Every thing. + // Format of amount + $response =& $this->_response; + if(!empty($response->amount) && $response->currency != 'TWD') { + $response->amount = (float)$response->amount / 100; + } + + $apiResult = $this->_response->Result; + + // For AlterType API + $resultType = $apiResult->AlterType; + if (!empty($resultType)) { + $statusReverseMap = array_flip(CRM_Core_Payment_SPGATEWAY::$_statusMap); + $recurResult['contribution_status_id'] = $statusReverseMap[$resultType]; + } + if (!empty($result['NewNextTime'])) { + $recurResult['next_sched_contribution'] = $result['NewNextTime']; + } + + // For AlterOther API + if (!empty($apiResult->PeriodType)) { + $unitReverseMap = array_flip(CRM_Core_Payment_SPGATEWAY::$_unitMap); + $recurResult['frequency_unit'] = $unitReverseMap[$apiResult->PeriodType]; + } + $result['cycle_day'] = $apiResult->PeriodPoint; + if (!empty($apiResult->NewNextAmt) && $apiResult->NewNextAmt != '-') { + $recurResult['amount'] = $apiResult->NewNextAmt; + } + if (!empty($apiResult->NewNextTime)) { + $recurResult['next_sched_contribution'] = $apiResult->NewNextTime; + } + if (!empty($apiResult->PeriodTimes)) { + $recurResult['installments'] = $apiResult->PeriodTimes; + } + + return $recurResult; + } + else if ( in_array($this->_response->Status, array('PER10061', 'PER10063', 'PER10062', 'PER10064')) ) { + // Refs #30842, Status is already changed in NewebPay. + $recurResult['response_status'] = $this->_response->Status; + $recurResult['msg'] = $recurResult['note_body'] = ts('NewebPay response:') . $this->_response->Message; + return $recurResult; + } + else { + $errResult['is_error'] = 1; + $errResult['msg'] = $this->_response->Status.': '.$this->_response->Message; + CRM_Core_Error::debug('Spgateway_RecurChangeAPI_errResult', $errResult); + return $errResult; + } + + } + else if ($result['success'] == FALSE && $result['status'] == 0){ + // Curl Error + $return = array( + 'is_error' => 1, + 'msg' => reset($result['curlError']), + ); + return $return; + } + else if (empty($this->_response)) { + // No any response, need to ask Newebpay + CRM_Core_Error::debug('NewebPay api request as empty response', $this); + $return = array( + 'is_error' => 1, + 'msg' => ts('The response from payment provider is empty.'), + ); + return $return; + } + else { + CRM_Core_Error::debug('NewebPay api request else', $this); + return FALSE; + } + } + + private function _request() { + $this->_success = FALSE; + if (!empty(getenv('CIVICRM_TEST_DSN'))) { + return array( + 'success' => FALSE, + 'status' => NULL, + 'curlError' => NULL, + ); + } + $ch = curl_init($this->_apiURL); + $opt = array(); + $opt[CURLOPT_RETURNTRANSFER] = TRUE; + $opt[CURLOPT_SSL_VERIFYPEER] = FALSE; + if($this->_apiMethod == 'POST'){ + $requestString = http_build_query($this->_request, '', '&'); + $postDataString = self::recurEncrypt($requestString, $this->_paymentProcessor); + $postFields = array( + 'MerchantID_' => $this->_paymentProcessor['user_name'], + 'PostData_' => $postDataString, + ); + $opt[CURLOPT_POST] = TRUE; + $opt[CURLOPT_POSTFIELDS] = $postFields; + } + curl_setopt_array($ch, $opt); + + $result = curl_exec($ch); + + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $errno = curl_errno($ch); + if (!empty($errno)) { + $errno = curl_errno($ch); + $err = curl_error($ch); + CRM_Core_Error::debug_log_message("CURL: $err :: $errno"); + } + + if ($result === FALSE) { + $errno = curl_errno($ch); + $err = curl_error($ch); + $curlError = array($errno => $err); + } + else{ + $curlError = array(); + } + curl_close($ch); + if (!empty($result)) { + $response = json_decode($result); + $this->_response = json_decode(self::recurDecrypt($response->period, $this->_paymentProcessor)); + $this->_success = isset($response->status) && $response->status == '0' ? TRUE : FALSE; + } + else { + $this->_response = NULL; + } + $return = array( + 'success' => $this->_success, + 'status' => $status, + 'curlError' => $curlError, + ); + return $return; + } + + + /** + * Migrate from civicrm_spgateway_record + * + * @param int $cid + * @param array $data + * @return void + */ + public static function writeRecord($cid, $data = null){ + if(is_numeric($cid)){ + if(empty($data) && !empty($_POST)){ + $data = $_POST; + } + + if(!empty($data['JSONData'])){ + $data = $data['JSONData']; + } + $exists = CRM_Core_DAO::singleValueQuery("SELECT cid FROM civicrm_contribution_spgateway WHERE cid = %1", array( + 1 => array($cid, 'Integer'), + )); + if ($exists) { + CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_spgateway SET data = %1 WHERE cid = %2", array( + 1 => array(json_encode($data), 'String'), + 2 => array($cid, 'Integer') + )); + } + else { + CRM_Core_DAO::executeQuery("INSERT INTO civicrm_contribution_spgateway (cid, data) VALUES(%2, %1) ", array( + 1 => array(json_encode($data), 'String'), + 2 => array($cid, 'Integer') + )); + } + + // Set expire time + $dataObj = self::dataDecode($data); + if(!empty($dataObj['ExpireDate'])){ + $expire_date = $dataObj['ExpireDate']; + if(!empty($dataObj['ExpireTime'])){ + $expire_date .= ' '.$dataObj['ExpireTime']; + } + else{ + $expire_date .= ' 23:59:59'; + } + } + if(!empty($expire_date)){ + $sql = "UPDATE civicrm_contribution SET expire_date = %1 WHERE id = %2"; + $params = array( + 1 => array( $expire_date, 'String'), + 2 => array( $cid, 'Integer'), + ); + CRM_Core_DAO::executeQuery($sql, $params); + } + } + } + + /** + * Migrate from _civicrm_spgateway_post_decode + * + * Decode JSON data before save from Spgateway / neweb response + * + * @param array|object $post + * @return void + */ + public static function dataDecode($post = null){ + $data = empty($post) ? $_POST : $post; + if (is_object($data)) { + $data = (array) $data; + } + if (is_array($data) && !empty($data['JSONData'])){ + // decode JSONData + $data = $data['JSONData']; + } + if (is_string($data) && json_decode($data)){ + $data = json_decode($data, TRUE); + if (is_string($data) && json_decode($data)) { + // Sometimes, neweb will return 2 times encode json. + $data = json_decode($data, TRUE); + } + } + + $return = $data; + + // flatten the jsonData object to 1-dimension array. + if(isset($data['Result'])){ + if(is_string($data['Result']) && json_decode($data['Result'], true)){ + $return = $dataResult = json_decode($data['Result'], true); + } + else { + $return = $dataResult = (array) $data['Result']; + } + if (!empty($data['Status'])) { + if (empty($return['Status'])) { + $return['Status'] = $data['Status']; + // status is in origin data, not in 'Result' object. + } + else { + $return['_RequestStatus'] = $data['Status']; + // The condition jsonData status is success, but the error status is in 'Result' attribute. + } + } + if (empty($dataResult['Message']) && !empty($data['Message'])) { + // 'Result' has no 'Message', use original 'Message'. + $return['Message'] = $data['Message']; + } + } + return $return; + } + + /** + * Prepare available fields for spgateway + * + * @param string $apiType + * @param bool $required + * @return array + */ + public static function getRequestFields($apiType, $required = FALSE) { + $fields = array(); + switch($apiType){ + case 'alter-status': + $fields = explode(',', 'RespondType*,Version*,MerOrderNo*,PeriodNo*,AlterType*,TimeStamp*'); + break; + case 'alter-amt': + $fields = explode(',', 'RespondType*,Version*,TimeStamp*,MerOrderNo*,PeriodNo*,AlterAmt,PeriodType,PeriodPoint,PeriodTimes,Extday'); + break; + } + foreach ($fields as $key => &$value) { + if(!strstr($value, '*') && $required) { + unset($fields[$key]); + } + else{ + $value = str_replace('*', '', $value); + } + } + return $fields; + } + + /** + * Migrate from _civicrm_spgateway_postdata + * + * @param string $url + * @param array $post_data + * @return object + */ + public static function sendRequest($url, $post_data){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + curl_setopt($ch, CURLOPT_HEADER, 0); // DO NOT RETURN HTTP HEADERS + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // RETURN THE CONTENTS OF THE CALL + $response = curl_exec($ch); + if(curl_errno($ch)){ + CRM_Core_Error::debug_log_message('civicrm_spgateway: Fetch recuring error: curl_errno: '.curl_errno($ch).' / '. curl_error($ch), 'error'); + } + else{ + $field_string = http_build_query($post_data, '', '&'); + CRM_Core_Error::debug_log_message('civicrm_spgateway: Request:'.$url."?".$field_string); + CRM_Core_Error::debug_log_message('civicrm_spgateway: Response:'.$response); + } + curl_close($ch); + if(!empty($response)){ + return json_decode($response); + } + else{ + return FALSE; + } + } + + /** + * Migrate from _civicrm_spgateway_encode + * + * Encode spgateway / neweb API delivery + * + * @param array $args + * @param object $payment_processor + * @param array $checkArgs + * @return string + */ + public static function encode(&$args, $payment_processor, $checkArgs = array()){ + // remove empty arg + if(is_array($args)){ + foreach($args as $k => $v){ + if($k == 'CheckValue'){ + unset($args[$k]); + } + } + } + elseif(is_string($args)){ + $tmp = explode('&', $args); + $args = array(); + foreach($tmp as $v){ + list($key, $value) = explode('=', $v); + $args[$key] = $value; + } + } + if(count($checkArgs) == 0){ + $checkArgs = array('HashKey','Amt','MerchantID','MerchantOrderNo','TimeStamp','Version','HashIV'); + } + foreach($checkArgs as $k){ + switch ($k) { + case 'HashIV': + case 'IV': + $v = $payment_processor['signature']; + self::checkKeyIV($v); + break; + case 'HashKey': + case 'Key': + $v = $payment_processor['password']; + self::checkKeyIV($v); + break; + default: + $v = $args[$k]; + break; + } + $a[] = $k.'='.$v; + } + $keystr = implode('&', $a); + + $checkvalue = strtoupper(hash("sha256", $keystr)); + $args['CheckValue'] = $checkvalue; + return $checkvalue; + } + + /** + * Migrate from _civicrm_spgateway_checkKeyIV + */ + public static function checkKeyIV($v){ + if(empty($v)){ + CRM_Core_Error::fatal(ts('KEY and IV should have value.')); + } + } + + /** + * Migrate from _civicrm_spgateway_checkmacvalue + * + * @param array $args + * @param object $payment_processor + * @return string + */ + public static function checkMacValue(&$args, $payment_processor){ + $used_args = array('HashKey','Amt','MerchantID','MerchantOrderNo','TimeStamp','Version','HashIV'); + return self::encode($args, $payment_processor, $used_args); + } + + /** + * Migrate from _civicrm_spgateway_checkcode + * + * @param array $args + * @param object $payment_processor + * @return string + */ + public static function checkCode(&$args, $payment_processor){ + $used_args = array('HashIV','Amt','MerchantID','MerchantOrderNo','TradeNo','HashKey'); + return self::encode($args, $payment_processor, $used_args); + } + + /** + * Migrate from _civicrm_spgateway_recur_encrypt + * + * @param string $str + * @param object $payment_processor + * @return string + */ + public static function recurEncrypt($str, $payment_processor){ + $key = $payment_processor['password']; + $iv = $payment_processor['signature']; + self::checkKeyIV($key); + self::checkKeyIV($iv); + $str = trim(self::encrypt($key, $iv, $str)); + return $str; + } + + /** + * Migrate from _civicrm_spgateway_recur_decrypt + * + * @param string $str + * @param object $payment_processor + * @return string + */ + public static function recurDecrypt($str, $payment_processor){ + $key = $payment_processor['password']; + $iv = $payment_processor['signature']; + self::checkKeyIV($key); + self::checkKeyIV($iv); + $str = self::decrypt($key, $iv, $str); + return $str; + } + + /** + * Migrate from _civicrm_spgateway_encrypt + * + * @param string $key + * @param string $iv + * @param string $str + * @param bool $force + * + * @return string + */ + public static function encrypt($key, $iv, $str, $force = NULL) { + $data = self::addPadding($str); + if ($force) { + if ($force == 'openssl') { + $openssl = TRUE; + } + elseif($force == 'mcrypt') { + $mcrypt = TRUE; + } + } + else { + $openssl = extension_loaded('openssl') ? TRUE : FALSE; + $mcrypt = extension_loaded('mcrypt') ? TRUE : FALSE; + } + if (empty($openssl) && empty($mcrypt)) { + return FALSE; + } + + if ($openssl) { + $keyLen = strlen($key); + switch($keyLen) { + case 16: + $encoding = 'AES-128-CBC'; + break; + case 24: + $encoding = 'AES-192-CBC'; + break; + case 32: + default: + $encoding = 'AES-256-CBC'; + break; + } + $encrypted = openssl_encrypt($data, $encoding, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); + } + elseif ($mcrypt) { + $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv); + } + else { + return ''; + } + return bin2hex($encrypted); + } + + /** + * Migrate from _civicrm_spgateway_decrypt + * + * @param string $key + * @param string $iv + * @param string $encrypted + * @param bool $force + * @return void + */ + public static function decrypt($key, $iv, $encrypted, $force = NULL) { + if ($force) { + if ($force == 'openssl') { + $openssl = TRUE; + } + elseif($force == 'mcrypt') { + $mcrypt = TRUE; + } + } + else { + $openssl = extension_loaded('openssl') ? TRUE : FALSE; + $mcrypt = extension_loaded('mcrypt') ? TRUE : FALSE; + } + if (empty($openssl) && empty($mcrypt)) { + return FALSE; + } + + $data = hex2bin($encrypted); + if ($openssl) { + $keyLen = strlen($key); + switch($keyLen) { + case 16: + $encoding = 'AES-128-CBC'; + break; + case 24: + $encoding = 'AES-192-CBC'; + break; + case 32: + default: + $encoding = 'AES-256-CBC'; + break; + } + $decrypted = openssl_decrypt($data, $encoding, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); + } + elseif ($mcrypt) { + $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv); + } + return self::stripPadding($decrypted); + } + + /** + * Migrate from _civicrm_spgateway_addpadding + * + * @param string $string + * @param int $blocksize + * @return string + */ + public static function addPadding($string, $blocksize = 32) { + $len = strlen($string); + $pad = $blocksize - ($len % $blocksize); + $string .= str_repeat(chr($pad), $pad); + return $string; + } + + /** + * Migrate from _civicrm_spgateway_strippadding + * + * @param string $string + * @return bool|string + */ + public static function stripPadding($string) { + $slast = ord(substr($string, -1)); + $slastc = chr($slast); + if (preg_match("/$slastc{" . $slast . "}/", $string)) { + $string = substr($string, 0, strlen($string) - $slast); + return $string; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/CRM/Core/Payment/SPGATEWAYIPN.php b/CRM/Core/Payment/SPGATEWAYIPN.php new file mode 100644 index 000000000..5ebd161b8 --- /dev/null +++ b/CRM/Core/Payment/SPGATEWAYIPN.php @@ -0,0 +1,337 @@ +_post = $post; + $this->_get = $get; + } + + function main($instrument){ + $objects = $ids = $input = array(); + $this->getIds($ids); + if(empty($ids['contributionRecur'])){ + $recur = FALSE; + // get the contribution and contact ids from the GET params + $input = CRM_Core_Payment_SPGATEWAYAPI::dataDecode($this->_post); + CRM_Core_Payment_SPGATEWAYAPI::writeRecord($ids['contribution'], $this->_post); + } + else{ + $recur = TRUE; + // Refs #35316, recur.proessor_id => contribution.payment_processor_id => $_GET['ppid'] + $sql = 'SELECT processor_id FROM civicrm_contribution_recur WHERE id = %1'; + $ppid = CRM_Core_DAO::singleValueQuery($sql, array(1 => array($ids['contributionRecur'], 'Integer'))); + if (empty($ppid)) { + $sql = 'SELECT payment_processor_id FROM civicrm_contribution WHERE id = %1'; + $ppid = CRM_Core_DAO::singleValueQuery($sql, array(1 => array($ids['contribution'], 'Integer'))); + } + if (empty($ppid)) { + CRM_Core_Error::debug_log_message("Spgateway: could not find payment processor id on this contribution {$ids['contribution']}"); + CRM_Utils_System::civiExit(); + } + $isTest = CRM_Core_DAO::singleValueQuery("SELECT is_test FROM civicrm_payment_processor WHERE id = %1", array( + 1 => array($ppid, 'Integer'), + )); + $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($ppid, $isTest ? 'test' : 'live'); + $this->_post = CRM_Core_Payment_SPGATEWAYAPI::recurDecrypt($this->_post['Period'], $paymentProcessor); + $input = CRM_Core_Payment_SPGATEWAYAPI::dataDecode($this->_post); + // we will save record later if this is recurring after second times. + if(empty($input['AlreadyTimes'])){ + // First time recurring + CRM_Core_Payment_SPGATEWAYAPI::writeRecord($ids['contribution'], $this->_post); + } + } + $input['component'] = !empty($ids['participant']) ? 'event' : 'contribute'; + + // now, retrieve full object by validateData, or false fallback + // when it's recurring, this will load first recurring contrib into object even it's not + if (!$this->validateData( $input, $ids, $objects ) ) { + return FALSE; + } + + // set global variable for paymentProcessor + $this->_paymentProcessor = $objects['paymentProcessor']; + + if($objects['contribution']->contribution_status_id == 1 && empty($input['AlreadyTimes'])){ + // already completed, skip + return '1|OK'; + } + else{ + // skip doing job when contribution already success + if(!empty($input['AlreadyTimes']) && $input['Status'] === 'SUCCESS'){ + $newTrxnId = $input['OrderNo']; + if ($newTrxnId == $input['MerchantOrderNo'] . "_1") { + if($objects['contribution']->contribution_status_id == 1){ + CRM_Core_Error::debug_log_message("Spgateway: The transaction {$newTrxnId}, associated with the contribution {$objects['contribution']->trxn_id}, has been successfully processed before. Skipped."); + return '1|OK'; + } + } + else { + $alreadySuccessId = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contribution WHERE trxn_id = %1 AND contribution_status_id = 1", array( + 1 => array($newTrxnId, 'String'), + )); + if (!empty($alreadySuccessId)) { + CRM_Core_Error::debug_log_message("Spgateway: The transaction {$newTrxnId}, associated with the contribution {$alreadySuccessId}, has been successfully processed before. Skipped."); + return '1|OK'; + } + } + } + // start validation + $note = ''; + if( $this->validateOthers($input, $ids, $objects, $note, $instrument) ){ + $contribution =& $objects['contribution']; + if(empty($contribution->receive_date)){ + if(!empty($input['PayTime'])){ + $contribution->receive_date = date('YmdHis', strtotime($input['PayTime'])); + }elseif(!empty($input['AuthTime'])){ + $contribution->receive_date = date('YmdHis', strtotime($input['AuthTime'])); + }elseif(!empty($input['AuthDate'])){ + $contribution->receive_date = date('YmdHis', strtotime($input['AuthDate'])); + }else{ + $contribution->receive_date = date('YmdHis'); + } + } + + // assign trxn_id before complete transaction + $recurContribCount = NULL; + if (!empty($contribution->contribution_recur_id)) { + $recurContribCount = CRM_Core_DAO::singleValueQuery("SELECT count(id) FROM civicrm_contribution WHERE contribution_recur_id = %1", array( + 1 => array($contribution->contribution_recur_id, 'Integer') + )); + } + if(!empty($input['PeriodAmt']) && $recurContribCount == 1){ + // first contribution of recurring, update trxn_id. + $input['trxn_id'] = $input['MerchantOrderNo'] . "_1"; + } + else { + $input['trxn_id'] = $objects['contribution']->trxn_id; + } + $transaction = new CRM_Core_Transaction(); + $this->completeTransaction( $input, $ids, $objects, $transaction, $recur ); + $note .= ts('Completed')."\n"; + $this->addNote($note, $contribution); + return '1|OK'; + } + else{ + $rtnCode = !empty($input['RtnCode']) ? $input['RtnCode'] : ''; + $rtnMsg = !empty($input['RtnMsg']) ? $input['RtnMsg'] : ''; + $note .= ts('Failed')."\n"; + $note .= ts("Payment Information").": ".ts("Failed").' - '.$rtnMsg."({$rtnCode})"; + $this->addNote($note, $objects['contribution']); + return ''; + } + } + + // error stage: doesn't goto and not the background posturl + // never for front-end user. + return FALSE; + } + + function getIds(&$ids){ + $contribId = CRM_Utils_Array::value('cid', $this->_get); + if (!empty($contribId) && CRM_Utils_Type::escape($contribId, 'Integer')) { + $ids = CRM_Contribute_BAO_Contribution::buildIds($contribId, FALSE); + if (empty($ids)) { + CRM_Core_Error::debug_log_message("Spgateway: Could not found contribution id $contribId"); + CRM_Utils_System::civiExit(); + } + } + // component is contribute + if (empty($ids['participant'])) { + if (!empty($this->_get['rid'])) { + $ids['related_contact'] = CRM_Utils_Array::value('rid', $this->_get); + } + if (!empty($this->_get['onbehalf_dupe_alert'])) { + $ids['onbehalf_dupe_alert'] = CRM_Utils_Array::value('onbehalf_dupe_alert', $this->_get); + } + } + } + + function validateOthers( &$input, &$ids, &$objects, &$note, $instrument = ''){ + $contribution = &$objects['contribution']; + $pass = TRUE; + $validValue = array(); + if(!empty($input['MerchantOrderNo'])){ + $validValue['MerchantOrderNo'] = $input['MerchantOrderNo']; + }else{ + $validValue['MerchantOrderNo'] = $input['MerOrderNo']; + } + if(!empty($ids['contributionRecur'])){ + if(!empty($input['AuthAmt'])){ + // after the first recurring + $validValue['Amt'] = $input['AuthAmt']; + if ($contribution->total_amount != $validValue['Amt']) { + $note .= ts("Amount values dont match between database and IPN request. Force use IPN instead {$contribution->trxn_id}-{$input['AlreadyTimes']} . Original: {$contribution->total_amount}, IPN:{$validValue['Amt']}")."\n"; + $contribution->total_amount = $validValue['Amt']; + } + } + else{ + // first recurring + $validValue['Amt'] = !empty($input['PeriodAmt']) ? $input['PeriodAmt'] : '0'; + } + } + else{ + $validValue['Amt'] = !empty($input['Amt']) ? $input['Amt'] : '0'; + $validValue['CheckCode'] = !empty($input['CheckCode']) ? $input['CheckCode'] : ''; + } + + // check contribution id matches + // If is from old neweb. Skip check. + // if it's recurring, the merchant order not should be first recurring no + if ( !strstr($contribution->trxn_id, $validValue['MerchantOrderNo']) && !preg_match('/^99[\d]{7}$/', $validValue['MerchantOrderNo'])) { + CRM_Core_Error::debug_log_message("Spgateway: OrderNumber values doesn't match between database and IPN request. {$contribution->trxn_id} : {$validValue['MerchantOrderNo']} " ); + $note .= ts("Failuare: OrderNumber values doesn't match between database and IPN request. {$contribution->trxn_id} : {$validValue['MerchantOrderNo']}")."\n"; + $pass = FALSE; + } + + // check amount + if ( round($contribution->total_amount) != $validValue['Amt'] && $input['Status'] == 'SUCCESS' ) { + CRM_Core_Error::debug_log_message("Spgateway: Amount values dont match between database and IPN request. {$contribution->trxn_id}-{$input['AlreadyTimes']} : {$validValue['Amt']}" ); + $note .= ts("Failuare: Amount values dont match between database and IPN request. {$contribution->trxn_id}-{$input['AlreadyTimes']} : {$validValue['Amt']}")."\n"; + $pass = FALSE; + } + + // spgateway validation + // only validate this when not test. + if (strtolower($instrument) == 'googlepay') { + $ppid = $this->_paymentProcessor['user_name']; + $test = $contribution->is_test ? 'test':'live'; + $paymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($ppid, $test); + } + else { + $paymentProcessor = $this->_paymentProcessor; + } + if(!empty($validValue['CheckCode'])){ + $checkCode = CRM_Core_Payment_SPGATEWAYAPI::checkCode($input, $paymentProcessor); + if(strtolower($validValue['CheckCode']) != strtolower($checkCode)) { + $note .= ts("Failuare: CheckCode not match. Contact system admin.")."\n"; + CRM_Core_Error::debug_log_message("Spgateway: Failuare: CheckCode not match. Should be '{$checkCode}', but '{$input['CheckCode']}' provided."); + $pass = FALSE; + } + } + + // recurring validation + if(!empty($ids['contributionRecur'])){ + $recur = &$objects['contributionRecur']; + $params = $null = array(); + // see if we are first time, if not first time, save new contribution + // 6 - expired + // 5 - processing + // 4 - fail + // 3 - canceled + // 2 - pending + // 1 - completed + + // not the first time (PeriodReturnURL) + if(!empty($input['AlreadyTimes'])){ + $trxn_id = $input['OrderNo']; + if($input['Status'] != 'SUCCESS'){ + $contribution->contribution_status_id = 4; // Failed + $c = self::copyContribution($contribution, $ids['contributionRecur'], $trxn_id); + } + else{ + $contribution->contribution_status_id = 1; // Completed + // Check if trxn_id is existed or not. + $alreadyExistsId = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contribution WHERE trxn_id = %1", array( + 1 => array($trxn_id, 'String'), + )); + if (!$alreadyExistsId) { + // Trxn_id is not existed, clone contribution. + $c = self::copyContribution($contribution, $ids['contributionRecur'], $trxn_id); + } + else { + // Sync contribution from given trxn_id. + // Will only process this when contribution status is not success. + $c = new CRM_Contribute_DAO_Contribution(); + $c->id = $alreadyExistsId; + $c->find(TRUE); + } + } + if(!empty($c)){ + unset($objects['contribution']); + CRM_Core_Payment_SPGATEWAYAPI::writeRecord($c->id, $this->_post); + $objects['contribution'] = $c; + if(isset($input['TotalTimes']) && $input['AlreadyTimes'] == $input['TotalTimes']){ + $recurParam = array( + 'id' => $ids['contributionRecur'], + 'modified_date' => date('YmdHis'), + 'end_date' => date('YmdHis'), + 'contribution_status_id' => 1, // completed + ); + CRM_Contribute_BAO_ContributionRecur::add($recurParam, $null); + } + } + } + else{ + // is first time + if($input['Status'] == 'SUCCESS'){ + $params['id'] = $recur->id; + $params['start_date'] = date('YmdHis', strtotime($input['AuthTime'])); + $params['contribution_status_id'] = 5; // from pending to processing + $params['modified_date'] = date('YmdHis'); + $params['trxn_id'] = $input['PeriodNo']; + if (!empty($input['AuthTimes']) && $input['AuthTimes'] != $params['installments']) { + $params['installments'] = $input['AuthTimes']; + $params['message'] = ts('Modify installments by Newebpay data.'); + CRM_Contribute_BAO_ContributionRecur::addNote($recur->id, $params['message']); + } + CRM_Contribute_BAO_ContributionRecur::add($params, $null); + } + else{ + CRM_Contribute_BAO_ContributionRecur::cancelRecurContribution($recur->id, CRM_Core_DAO::$_nullObject, 4); + } + } + } + + // process fail response + if($input['Status'] != 'SUCCESS' && $pass){ + $responseCode = $input['Status']; + $responseMsg = $input['Message']."\n"._civicrm_spgateway_error_msg($responseCode); + if ($input['Status'] == 'Error' && !empty($input['RespondCode'])) { + $responseCode .= ': ' . $input['RespondCode']; + } + $failedReason = $responseMsg. ' ('.ts('Error Code:'). $responseCode.')'; + $note .= $failedReason; + if ($input['PayTime']) { + $objects['contribution']->cancel_date = $input['PayTime']; + } + elseif ($input['CreateTime']) { + $objects['contribution']->cancel_date = $input['CreateTime']; + } + elseif ($input['AuthDate']) { + $objects['contribution']->cancel_date = $input['AuthDate']; + } + $transaction = new CRM_Core_Transaction(); + $this->failed($objects, $transaction, $failedReason); + $pass = FALSE; + } + + return $pass; + } + + function addNote($note, &$contribution){ + $note = date("Y/m/d H:i:s"). ts("Transaction record").": \n".$note."\n===============================\n"; + $noteExists = CRM_Core_BAO_Note::getNote( $contribution->id, 'civicrm_contribution' ); + if(count($noteExists)){ + $noteId = array( 'id' => reset(array_keys($noteExists)) ); + $note = $note . reset($noteExists); + } + else{ + $noteId = NULL; + } + + $noteParams = array( + 'entity_table' => 'civicrm_contribution', + 'note' => $note, + 'entity_id' => $contribution->id, + 'contact_id' => $contribution->contact_id, + 'modified_date' => date('Ymd') + ); + CRM_Core_BAO_Note::add($noteParams, $noteId); + } +} \ No newline at end of file diff --git a/CRM/Core/Payment/SPGATEWAYNeweb.php b/CRM/Core/Payment/SPGATEWAYNeweb.php new file mode 100644 index 000000000..c84fb356d --- /dev/null +++ b/CRM/Core/Payment/SPGATEWAYNeweb.php @@ -0,0 +1,291 @@ + array($rid, 'Positive')); + $ipn_result['Result']['MerchantOrderNo'] = CRM_Core_DAO::singleValueQuery("SELECT trxn_id FROM civicrm_contribution WHERE contribution_recur_id = %1 ORDER BY id ASC LIMIT 1", $queryParams); + $ipn_result['Status'] = $decryptParams['Status']; + $ipn_result['Result']['OrderNo'] = 'r_'.$ipn_result['Result']['OrderNo']; + $periodNo = $ipn_result['Result']['PeriodNo']; + CRM_Core_Error::debug_var('spgateway_neweb_transfer_ipn_result', $ipn_result); + $ipn_result = json_encode($ipn_result); + $ipn_post = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt($ipn_result, $paymentProcessor)); + CRM_Core_Error::debug_var('spgateway_neweb_transfer_ipn_post', $ipn_post); + + // prepare get + $firstCid = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contribution WHERE contribution_recur_id = %1 ORDER BY id ASC LIMIT 1", $queryParams); + $ids = CRM_Contribute_BAO_Contribution::buildIds($firstCid); + $query = CRM_Contribute_BAO_Contribution::makeNotifyUrl($ids, NULL, TRUE); + $query .= '&ppid='.$pid; + parse_str($query, $ipn_get); + CRM_Core_Error::debug_var('spgateway_neweb_transfer_ipn_get', $ipn_get); + + // create recurring record + $result = new stdClass(); + $result->_ipn_result = $ipn_result; + $result->_post = $ipn_post; + $result->_get = $ipn_get; + $result->_response = CRM_Core_Payment_SPGATEWAY::doIPN(array('spgateway', 'ipn', 'Credit'), $ipn_post, $ipn_get, FALSE); + // If correct, it must return '1|OK'. + if (!empty($result->_response)) { + $query = "UPDATE civicrm_contribution SET payment_processor_id = %1 WHERE contribution_recur_id = %2 ORDER BY id DESC LIMIT 1"; + $params = array( + 1 => array($pid, 'Positive'), + 2 => array($ids['contributionRecurID'], 'Positive'), + ); + CRM_Core_DAO::executeQuery($query, $params); + // Check processor_id, trxn_id of contribution_recur + $sql = "SELECT trxn_id, processor_id FROM civicrm_contribution_recur WHERE id = %1"; + $sqlParams = array( 1 => array($ids['contributionRecurID'], 'Positive')); + $recurDao = CRM_Core_DAO::executeQuery($sql, $sqlParams); + while ($recurDao->fetch()) { + if ($recurDao->processor_id != $pid) { + $recurParams['processor_id'] = $pid; + } + if ($recurDao->trxn_id != $periodNo) { + $recurParams['trxn_id'] = $periodNo; + } + if (!empty($recurParams)) { + $recurParams['id'] = $ids['contributionRecurID']; + $recurParams['message'] = ts('Modify parameters of original Newebpay by Newebpay data.'); + CRM_Core_Error::debug_var('spgateway_neweb_transfer_update_recur_params', $recurParams); + CRM_Contribute_BAO_ContributionRecur::addNote($ids['contributionRecurID'], $recurParams['message']); + CRM_Contribute_BAO_ContributionRecur::add($recurParams, $null); + } + } + } + CRM_Core_Error::debug_var('spgateway_neweb_transfer_ipn_response', $result->_response); + echo $result->_response; + } + else { + $msg = 'Error. Decrypt error or Recurring ID is not found.'; + CRM_Core_Error::debug_log_message('civicrm_spgateway.neweb: '.$msg); + echo $msg; + } + CRM_Utils_System::civiExit(); + } + + + /** + * Migrate from civicrm_spgateway_neweb_resync + * + * @param bool $ppid_new + * @param string $day + * @param array $recurNo + * @return void + */ + public static function resync($ppid_new, $day = '', $recurNo = array()) { + CRM_Core_Error::debug_log_message("Start doResyncOldNewebRecur, ppid_new = {$ppid_new}, day = {$day}"); + $payment_processor = CRM_Core_BAO_PaymentProcessor::getPayment($ppid_new, 'live'); + if (!empty($payment_processor)) { + + if (empty($recurNo)) { + $offset = CRM_Core_BAO_Cache::getItem('spgateway_neweb', 'resync_offset_'.$ppid_new); + $cycle_day = empty($day) ? date('j') : $day; + $sql = "SELECT DISTINCT r.id AS rid from civicrm_contribution_recur r INNER JOIN civicrm_contribution c ON r.id = c.contribution_recur_id WHERE c.trxn_id LIKE 'r_%' AND r.cycle_day = %1 AND c.payment_processor_id = %2 LIMIT 300 OFFSET %3"; + $params = array( + 1 => array($cycle_day, 'Integer'), + 2 => array($ppid_new, 'Integer'), + 3 => array($offset, 'Integer'), + ); + $bao = CRM_Core_DAO::executeQuery($sql, $params); + if ($bao->N == 0) { + // offset variable roll back to zero. + CRM_Core_BAO_Cache::setItem(0, 'spgateway_neweb', 'resync_offset_'.$ppid_new); + + // There are no recur need execute. return finished. + $message = "No more old neweb recurring need to sync {$ppid_new}"; + CRM_Core_Error::debug_log_message($message); + return TRUE; + } + while($bao->fetch()) { + $recurNo[] = $bao->rid; + } + } + $total = count($recurNo); + $skip = $count = $alreadyExist = $transactFailed = $success = $queryFailed = 0; + + foreach ($recurNo as $recurId) { + $sql = "SELECT id, last_receive_date , last_failed_date, ltid.last_trxn_id FROM civicrm_contribution_recur AS r + LEFT JOIN (SELECT contribution_recur_id AS rid, MAX(receive_date) AS last_receive_date FROM civicrm_contribution WHERE contribution_status_id = 1 AND contribution_recur_id = %1 GROUP BY contribution_recur_id) lrd ON lrd.rid = r.id + LEFT JOIN (SELECT contribution_recur_id AS rid, MAX(cancel_date) AS last_failed_date FROM civicrm_contribution WHERE contribution_status_id = 4 AND contribution_recur_id = %1 GROUP BY contribution_recur_id) lfd ON lfd.rid = r.id + LEFT JOIN (SELECT contribution_recur_id AS rid, CONCAT('r_', contribution_recur_id, '_', MAX(CAST(REGEXP_SUBSTR(trxn_id, '[0-9]+$') as INT))) AS last_trxn_id FROM civicrm_contribution WHERE contribution_recur_id = %1 AND trxn_id LIKE CONCAT('r_', contribution_recur_id, '_%') GROUP BY contribution_recur_id) ltid ON ltid.rid = r.id + WHERE r.id = %1"; + $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($recurId, 'Integer'))); + $dao->fetch(); + + // Only execute the contributions last month have successed. + $lastReceiveMonth = date('Ym', strtotime($dao->last_receive_date)); + $lastCancelMonth = date('Ym', strtotime($dao->last_failed_date)); + $lastExecuteMonth = max($lastReceiveMonth, $lastCancelMonth); + $thisMonth = date('Ym'); + $subtractMonth = $thisMonth - $lastExecuteMonth; + if (!in_array($subtractMonth, array(1, 89))) { + $skip++; + continue; + } + $output = 'Recur ID: '.$recurId; + CRM_Core_Error::debug_log_message("Find not complete contribution in this month, recur_id = {$recurId}, trxn_id = {$dao->last_trxn_id}, receive_date = {$dao->last_receive_date}, cancel_date = {$dao->last_failed_date}, last_execute_month = {$lastExecuteMonth}"); + $lastTrxnId = $dao->last_trxn_id; + $explodeTrxnId = explode('_', $lastTrxnId); + $no = $explodeTrxnId[2]+1; + $trxn_id = $recurId.'_'.$no; + $count++; + + $sql = "SELECT amount FROM civicrm_contribution_recur WHERE id = %1"; + $params = array( 1 => array($recurId, 'Positive')); + $amount = CRM_Core_DAO::singleValueQuery($sql, $params); + $amount = (int)$amount; + $request_data = array( + 'Amt' => $amount, + 'MerchantID' => $payment_processor['user_name'], + 'MerchantOrderNo' => $trxn_id, + 'RespondType' => CRM_Core_Payment_SPGATEWAY::RESPONSE_TYPE, + 'TimeStamp' => CRM_REQUEST_TIME, + 'Version' => CRM_Core_Payment_SPGATEWAY::QUERY_VERSION, + ); + CRM_Core_Error::debug_var("doResyncOldNewebRecur:request_data", $request_data); + $used_args = array('IV','Amt','MerchantID','MerchantOrderNo', 'Key'); + CRM_Core_Payment_SPGATEWAYAPI::encode($request_data, $payment_processor, $used_args); + $urlApi = CRM_Core_Payment_SPGATEWAY::REAL_DOMAIN.CRM_Core_Payment_SPGATEWAY::URL_API; + sleep(0.5); + CRM_Core_Error::debug_var("doResyncOldNewebRecur:encoded_request_data", $request_data); + $result = CRM_Core_Payment_SPGATEWAYAPI::sendRequest($urlApi, $request_data); + CRM_Core_Error::debug_var("doResyncOldNewebRecur:result", $result); + + if (!empty($result) && $result->Status == 'SUCCESS') { + $output .= ":QuerySuccess"; + echo ":QuerySuccess"; + // prepare contribution + $contribution = new CRM_Contribute_DAO_Contribution(); + $contribution->contribution_recur_id = $recurId; + $contribution->orderBy("id ASC"); + $contribution->find(TRUE); + if ($contribution->id && $contribution->contribution_recur_id == $recurId) { + $exists = CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM civicrm_contribution WHERE trxn_id LIKE 'r_{$trxn_id}'"); + if ($exists) { + $alreadyExist++; + $output .= ":DataExists:"; + $output .= $result->Result->TradeStatus."\n"; + echo ":DataExists:"; + echo $result->Result->TradeStatus."\n"; + continue; + } + // complex part to simulate spgateway ipn + $ipn_get = $ipn_post = array(); + + // prepare post, complex logic because recurring have different variable names + $ipn_result = clone $result; + if ($result->Result->TradeStatus != 1) { + $output .= ":TransactionFailed:".$result->Result->RespondMsg; + echo ":TransactionFailed:".$result->Result->RespondMsg; + $ipn_result->Status = 'Error'; + $transactFailed++; + } + else { + $output .= ":TransactionCompleted"; + echo ":TransactionCompleted"; + $success++; + } + $ipn_result->Message = $result->Result->RespondMsg; + $ipn_result->Result->AuthAmt = $result->Result->Amt; + unset($ipn_result->Result->Amt); + unset($ipn_result->Result->CheckCode); + $ipn_result->Result->OrderNo = $result->Result->MerchantOrderNo; + $ipn_result->Result->MerchantOrderNo = $recurId; + $ipn_result->Result->do_not_email = TRUE; + $ipn_result->Result->AlreadyTimes = 1; + CRM_Core_Error::debug_var('doResyncOldNewebRecur:ipn_result', $ipn_result); + $ipn_result = json_encode($ipn_result); + $ipn_post = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt($ipn_result, $payment_processor)); + CRM_Core_Error::debug_var('doResyncOldNewebRecur:ipn_post', $ipn_post); + self::transfer($ipn_post); + } + } + else { + var_dump($result); + $output .= ":QueryFailed"; + echo ":QueryFailed"; + $queryFailed++; + } + echo "\n"; + CRM_Core_Error::debug_log_message($output); + + } + $message = "cron result:"; + $start = $offset+1; + $message .= "From date {$start}. "; + if ($skip > 0) { + $message .= "Skip {$skip} records which already completed this month. "; + } + $message .= "Updated {$count} records. {$alreadyExist} records exists, {$transactFailed} records failed, {$success} records success, {$queryFailed} records query failed."; + if ($total > ($count+$skip)) { + $unexecuted = $total - $count - $skip; + $message .= ",{$unexecuted} records scheduled to next cron."; + } + CRM_Core_Error::debug_log_message($message); + CRM_Core_BAO_Cache::setItem($offset+300, 'spgateway_neweb', 'resync_offset_'.$ppid_new); + } + } +} diff --git a/CRM/Core/xml/Menu/Misc.xml b/CRM/Core/xml/Menu/Misc.xml index 8f62d576a..a9ca9c392 100644 --- a/CRM/Core/xml/Menu/Misc.xml +++ b/CRM/Core/xml/Menu/Misc.xml @@ -186,4 +186,18 @@ CRM_Core_Payment_SPGATEWAY::doSingleQueryRecord access CiviContribute + + spgateway/ipn + CRM_Core_Payment_SPGATEWAY::doIPN + 1 + true + true + + + spgateway/neweb-transfer + CRM_Core_Payment_SPGATEWAYNeweb::transfer + 1 + true + true + diff --git a/CRM/Import/Parser/Contact.php b/CRM/Import/Parser/Contact.php index d7975115e..483db5d31 100644 --- a/CRM/Import/Parser/Contact.php +++ b/CRM/Import/Parser/Contact.php @@ -1912,7 +1912,7 @@ function formatParams(&$params, $onDuplicate, $cid) { * @param string $dateParam index of params * @static */ - function formatCustomDate(&$params, &$formatted, $dateType, $dateParam) { + public static function formatCustomDate(&$params, &$formatted, $dateType, $dateParam) { //fix for CRM-2687 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $dateParam); diff --git a/CRM/Mailing/BAO/Mailing.php b/CRM/Mailing/BAO/Mailing.php index 9332491af..7b1b0fc0f 100644 --- a/CRM/Mailing/BAO/Mailing.php +++ b/CRM/Mailing/BAO/Mailing.php @@ -2022,7 +2022,7 @@ public static function &report($id, $skipDetails = FALSE) { $row[$field] = $mailing->$field; } - if ($mailing->queue) { + if (!empty($mailing->queue) && !empty($mailing->delivered)) { $row['delivered_rate'] = (100.0 * $mailing->delivered) / $mailing->queue; $row['opened_rate'] = (100.0 * $row['opened']) / $mailing->delivered; $row['clicked_rate'] = (100.0 * $row['url']) / $mailing->delivered; diff --git a/CRM/Utils/Mail.php b/CRM/Utils/Mail.php index 6d9de3351..9e4c224d2 100644 --- a/CRM/Utils/Mail.php +++ b/CRM/Utils/Mail.php @@ -442,7 +442,7 @@ public static function checkRFC822Email($address) { $name = ''; $email = trim($address); } - if (!empty($name) && preg_match('/[' . preg_quote(self::RFC_2822_SPECIAL_CHARS) . ']/u', $name)) { + if (!empty($name) && preg_match('/[ ' . preg_quote(self::RFC_2822_SPECIAL_CHARS) . ']/u', $name)) { // check the name already quoted if (!preg_match('/^"[^"]+"\s*<[^>]+@[^>]+>/i', $address)) { return FALSE; diff --git a/CRM/Utils/Recent.php b/CRM/Utils/Recent.php index 31ea96d2b..63af8c3e5 100644 --- a/CRM/Utils/Recent.php +++ b/CRM/Utils/Recent.php @@ -64,7 +64,7 @@ static function initialize() { if (!self::$_recent) { $session = CRM_Core_Session::singleton(); self::$_recent = $session->get(self::STORE_NAME); - if (!self::$_recent) { + if (empty(self::$_recent) || (!empty(self::$_recent) && !is_array(self::$_recent))) { self::$_recent = array(); } } diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index c613e4da2..22e8d3355 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -172,6 +172,12 @@ static function url($url, $checkDomain = '', $checkHTTPS = NULL) { $url = $scheme.'://' . $_SERVER['HTTP_HOST'] . $url; } $valid = (bool) filter_var($url, FILTER_VALIDATE_URL); + if (!$valid) { + $parts = parse_url($url); + if (!empty($parts['scheme']) && !empty($parts['host'])) { + $valid = TRUE; + } + } if (!in_array(substr($url, 0, 5), array('http:', 'https'))) { $valid = FALSE; diff --git a/CRM/Utils/System.php b/CRM/Utils/System.php index cbf0af714..ef0e59896 100644 --- a/CRM/Utils/System.php +++ b/CRM/Utils/System.php @@ -1583,7 +1583,7 @@ public static function cmsDir($type) { /** * Get CMS public or private or temp dir * - * @return boolean. + * @return string */ public static function cmsRootPath() { return CRM_Core_Config::$_userSystem->cmsRootPath(); @@ -1697,22 +1697,24 @@ public static function sameSiteCheck() { $isUcBrowser = preg_match('/UCBrowser\//i', $useragent); if ($isUcBrowser) { - preg_match('/UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* /i', $useragent, $ucVersion); - if ($ucVersion[1] < 12) { // major - return FALSE; - } - if ($ucVersion[2] < 13) { // minor - return FALSE; - } - if ($ucVersion[3] < 2) { // buil - return FALSE; + if(preg_match('/UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* /i', $useragent, $ucVersion)) { + if ($ucVersion[1] < 12) { // major + return FALSE; + } + if ($ucVersion[2] < 13) { // minor + return FALSE; + } + if ($ucVersion[3] < 2) { // buil + return FALSE; + } } } if ($isChromiumBased) { - preg_match('/Chrome\/(\d+)\.(\d+)\.(\d+)[\.\d]* /i', $useragent, $chVersion); - if ($chVersion[1] >= 51 && $chVersion[1] <= 66) { - return FALSE; + if (preg_match('/Chrome\/(\d+)\.(\d+)\.(\d+)[\.\d]* /i', $useragent, $chVersion)) { + if ($chVersion[1] >= 51 && $chVersion[1] <= 66) { + return FALSE; + } } } return TRUE; diff --git a/CRM/Utils/System/Drupal.php b/CRM/Utils/System/Drupal.php index 30e6f2353..35faa4c2e 100644 --- a/CRM/Utils/System/Drupal.php +++ b/CRM/Utils/System/Drupal.php @@ -614,7 +614,8 @@ static function variable_get($name, $default) { if ($version < 8 ) { return variable_get($name, $default); } - else { + else{ + // no drupal 8+ equivilent func // exception } } diff --git a/api/v2/utils.v2.php b/api/v2/utils.v2.php index 3b238766b..b885a7435 100644 --- a/api/v2/utils.v2.php +++ b/api/v2/utils.v2.php @@ -103,8 +103,7 @@ function civicrm_create_success($result = 1) { */ function civicrm_duplicate($error) { if (is_array($error) && civicrm_error($error)) { - $code = $error['error_message']['code']; - if ($code == CRM_Core_Error::DUPLICATE_CONTACT) { + if (!empty($error['error_message']['code']) && $error['error_message']['code'] == CRM_Core_Error::DUPLICATE_CONTACT) { return TRUE; } } diff --git a/api/v3/Contact.php b/api/v3/Contact.php index 86c9f0ace..de621b3a2 100644 --- a/api/v3/Contact.php +++ b/api/v3/Contact.php @@ -917,4 +917,79 @@ function civicrm_api3_contact_checksum($params) { array($params['contact_id'] => $checksum), ); return civicrm_api3_create_success($values, $params, 'contact', 'checksum'); +} + +function civicrm_api3_contact_duplicatecheck($params) { + if (empty($params['contact_type'])) { + return civicrm_api3_create_error('Missing required field in params: contact_type.'); + } + if (empty($params['match'])) { + return civicrm_api3_create_error('Missing required fields in params: match.'); + } + + $dedupeParams = CRM_Dedupe_Finder::formatParams($params['match'], $params['contact_type']); + if (empty($dedupeParams['civicrm_contact'])) { + return civicrm_api3_create_error('Parameter check error. You need to specify at least 1 field to dedupe.'); + } + if (isset($params['check_permission'])) { + $dedupeParams['check_permission'] = $params['check_permission']; + } + + if (!empty($params['dedupe_rule_id'])) { + if (!is_numeric($params['dedupe_rule_id'])) { + return civicrm_api3_create_error('Please specify numeric rule id.'); + } + $rgBao = new CRM_Dedupe_BAO_RuleGroup(); + $rgBao->id = $params['dedupe_rule_id']; + $rgBao->contact_type = $params['contact_type']; + if (!$rgBao->find(TRUE)) { + return civicrm_api3_create_error('Please specify the correct rule ID corresponding to contact type.'); + } + $foundDupes = CRM_Dedupe_Finder::dupesByParams($dedupeParams, + $params['contact_type'], + 'Strict', + array(), + $params['dedupe_rule_id'] + ); + } + elseif(!empty($params['dedupe_rules']) && is_array($params['dedupe_rules'])) { + if (empty($params['threshold']) || !is_numeric($params['threshold']) || $params['threshold'] <= 0) { + return civicrm_api3_create_error('Please specify numeric threshold and greater than zero of this dedupe.'); + } + $supportedFields = CRM_Dedupe_BAO_RuleGroup::supportedFields($params['contact_type']); + foreach($params['dedupe_rules'] as $rule) { + if (!empty($rule['table']) && !empty($rule['field']) && !empty($rule['weight'])) { + if (!isset($supportedFields[$rule['table']])) { + return civicrm_api3_create_error('Rule table should be in supported table. Your rule table: '.$rule['table']." doesn't be supported."); + } + else { + if (!isset($supportedFields[$rule['table']][$rule['field']])) { + return civicrm_api3_create_error('Your rule table: '.$rule['table'].', field:'.$rule['field']." doesn't be supported."); + } + } + } + else { + return civicrm_api3_create_error('Each rule should have three required data: table,field,weight.'); + } + } + $foundDupes = CRM_Dedupe_Finder::dupesByRules( + $dedupeParams, + $params['contact_type'], + 'Strict', + array(), + $params['dedupe_rules'], + $params['threshold'] + ); + } + else { + $foundDupes = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $params['contact_type'], 'Strict', array()); + } + + if (empty($foundDupes)) { + $foundDupes = []; + } + if (count($foundDupes)) { + sort($foundDupes); + } + return civicrm_api3_create_success($foundDupes, $params, 'contact', 'duplicatecheck'); } \ No newline at end of file diff --git a/api/v3/utils.php b/api/v3/utils.php index 4ad9ceaf3..ee91ada70 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -715,7 +715,9 @@ function _civicrm_api3_get_options_from_params(&$params, $queryObject = false, $ function _civicrm_api3_apply_options_to_dao(&$params, &$dao, $entity) { $options = _civicrm_api3_get_options_from_params($params,false,$entity); - $dao->limit((int)$options['offset'], (int)$options['limit']); + if (!empty($options['limit'])) { + $dao->limit((int)$options['offset'], (int)$options['limit']); + } if (!empty($options['sort'])) { $dao->orderBy($options['sort']); } diff --git a/civicrm-version.txt b/civicrm-version.txt index ee2b15f99..6cb41c9e1 100644 --- a/civicrm-version.txt +++ b/civicrm-version.txt @@ -1 +1 @@ -6.0.9+8 (3957f57) +6.0.9+9 (e7cc3c4) diff --git a/drupal b/drupal index 966d85030..3cb5b18f3 160000 --- a/drupal +++ b/drupal @@ -1 +1 @@ -Subproject commit 966d850309fe39deea302b0f03805556c1e61b2e +Subproject commit 3cb5b18f384d1646042028e04abe7dba827c9b28 diff --git a/extern/url.php b/extern/url.php index ed644d770..02715a0bb 100644 --- a/extern/url.php +++ b/extern/url.php @@ -1,6 +1,6 @@ -{js src=packages/Magnific-Popup/dist/jquery.magnific-popup.min.js group=999 weight=997}{/js} +{js src=packages/Magnific-Popup/dist/jquery.magnific-popup.min.js group=999 weight=997 library=civicrm/civicrm-js-magnific-popup}{/js} +
diff --git a/templates/CRM/Event/Form/Registration/ThankYou.tpl b/templates/CRM/Event/Form/Registration/ThankYou.tpl index 385d8a66f..939a95b11 100644 --- a/templates/CRM/Event/Form/Registration/ThankYou.tpl +++ b/templates/CRM/Event/Form/Registration/ThankYou.tpl @@ -245,7 +245,7 @@ {ts 1=$customName+1}Participant Information - Participant %1{/ts}
{foreach from=$value item=val key=field} - {if $field eq additionalCustomPre or $field eq additionalCustomPost } + {if $field eq 'additionalCustomPre' or $field eq 'additionalCustomPost' } {if $field eq 'additionalCustomPre' }
{$value.additionalCustomPre_grouptitle} {else} diff --git a/tests/README.md b/tests/README.md index 81f3b2af7..95a6d3ae8 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,10 +1,13 @@ -Before testing, you need install following package: + +Before testing, you need to install the following packages: - phpunit -- casperjs +- casperjs (not maintained) +- playwright (Please refer to the Playwright section) # Unit Test + ``` cd civicrm mysql -udbuser -p -e "CREATE DATABASE civicrm_tests_dev CHARACTER SET utf8 COLLATE utf8_unicode_ci"; @@ -17,9 +20,11 @@ phpunit --colors api_v3_ContactTest phpunit --colors api_v3_AllTests ``` -# Casperjs -Make sure you have running website +# CasperJS + +Make sure you have a running website: + ``` export RUNPORT=80 @@ -28,3 +33,102 @@ casperjs test googletest.js casperjs test pagespages.js ``` + + +**Note: This is no longer maintained.** + +# Playwright + +To use our Playwright testing code to test your website, follow these steps: + +* Switch to our Playwright code directory: + + ``` + cd netiCRM/tests/playwright + ``` + +* Install the Playwright module: + + ``` + npm init playwright@latest + ``` + * For more details about installation, see the [official documentation](https://playwright.dev/docs/intro). + +* Install the dotenv module: + + ``` + cd netiCRM/tests/playwright + npm install dotenv --save + ``` + +* Set up the `setup.env` file: + * Rename the file `setup.example.env` to `setup.env` and modify the content according to your requirements: + ``` + # .env file + isLocal=true + localUrl=http://:/ + remoteUrl=http://:/ + adminUser=admin + adminPwd= # e.g., admin_password + ``` + +* Check the configuration file `playwright.config.js`: + * The above `setup.env` file is required here, and there is a `global-setup.js` file to handle logins, so you don't need to manually input your account and password every time (You can check the `global-setup.js` file). + * You can modify the following setup according to your requirements: + + ```javascript + const config = { + globalSetup: require.resolve('./global-setup'), + testDir: './tests', + timeout: 120 * 1000, + expect: { + timeout: 30 * 1000 + }, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [ + ['list'], + ['html', { open: 'never' }], + // ['./test-reporter.js'] + ], + use: { + headless: true, + actionTimeout: 30 * 1000, + storageState: 'storageState.json', + baseURL: process.env.localUrl, + trace: 'retain-on-failure', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + } + } + ], + outputDir: 'test-results/' + }; + ``` + +* Run Playwright commands: + + ``` + cd netiCRM/tests/playwright + // run a file + npx playwright test --project=chromium + // Check the report + npx playwright show-report + ``` + + * [More information](https://playwright.dev/docs/running-tests) + diff --git a/tests/phpunit/CRM/Core/Payment/ALLPAYTest.php b/tests/phpunit/CRM/Core/Payment/ALLPAYTest.php index ecf176fef..ca877d002 100644 --- a/tests/phpunit/CRM/Core/Payment/ALLPAYTest.php +++ b/tests/phpunit/CRM/Core/Payment/ALLPAYTest.php @@ -166,7 +166,7 @@ function testSinglePaymentNotify(){ 'TradeDate' => date('Y-m-d H:i:s', $now), 'SimulatePaid' => '1', ); - CRM_Core_Payment_ALLPAY::doIPN('Credit', $post, $get); + $this->doIPN(array('allpay', 'ipn', 'Credit'), $post, $get, __LINE__); // verify contribution status after trigger $this->assertDBCompareValue( @@ -266,7 +266,7 @@ function testRecurringPaymentNotify(){ 'TradeDate' => date('Y-m-d H:i:s', $now), 'SimulatePaid' => '1', ); - CRM_Core_Payment_ALLPAY::doIPN('Credit', $post, $get); + $this->doIPN(array('allpay', 'ipn', 'Credit'), $post, $get, __LINE__); // verify contribution status after trigger $this->assertDBCompareValue( @@ -313,7 +313,7 @@ function testRecurringPaymentNotify(){ 'TotalSuccessTimes' => 2, 'SimulatePaid' => '1', ); - CRM_Core_Payment_ALLPAY::doIPN('Credit', $post, $get); + $this->doIPN(array('allpay', 'ipn', 'Credit'), $post, $get, __LINE__); $trxn_id2 = CRM_Core_Payment_ALLPAY::generateRecurTrxn($trxn_id, $gwsr1); // check second payment contribution exists @@ -523,4 +523,18 @@ function testNonCreditNotify(){ CRM_Core_Payment_ALLPAYIPN::doRecordData(array('allpay', 'record', $cid)); $this->assertDBQuery($cid, "SELECT cid FROM civicrm_contribution_allpay WHERE data LIKE '%#info%TEST1%' AND cid = $cid"); } + + function doIPN($args, $post, $get, $line) { + try { + CRM_Core_Payment_ALLPAY::doIPN($args, $post, $get); + } + catch (CRM_Core_Exception $e) { + $message = $e->getMessage(); + $data = $e->getErrorData(); + $code = $e->getErrorCode(); + if ($code != CRM_Core_Error::NO_ERROR) { + throw new Exception($message.' at line '.$line, $code); + } + } + } } diff --git a/tests/phpunit/CRM/Core/Payment/SPGATEWAYTest.php b/tests/phpunit/CRM/Core/Payment/SPGATEWAYTest.php index 2bdeb2c9f..b43424eb4 100644 --- a/tests/phpunit/CRM/Core/Payment/SPGATEWAYTest.php +++ b/tests/phpunit/CRM/Core/Payment/SPGATEWAYTest.php @@ -9,27 +9,6 @@ class CRM_Core_Payment_SPGATEWAYTest extends CiviUnitTestCase { protected $_is_test; protected $_page_id; - /** - * Constructor - * - * Initialize configuration - */ - function __construct() { - // test if drupal bootstraped - if(!defined('DRUPAL_ROOT')){ - die("You must exprot DRUPAL_ROOT for bootstrap drupal before test."); - } - if(!CRM_Utils_System::moduleExists('civicrm_spgateway')){ - die("You must enable civicrm_spgateway module first before test."); - } - $payment_page = variable_get('civicrm_demo_payment_page', array()); - $class_name = 'Payment_SPGATEWAY'; - if(isset($payment_page[$class_name])){ - $this->_page_id = $payment_page[$class_name]; - } - parent::__construct(); - } - function get_info() { return array( 'name' => 'SPGATEWAY payment processor', @@ -44,6 +23,30 @@ function get_info() { function setUpTest() { parent::setUp(); + // Initialize configuration + if(!defined('DRUPAL_ROOT')){ + die("You must exprot DRUPAL_ROOT for bootstrap drupal before test."); + } + if(!CRM_Utils_System::moduleExists('civicrm_spgateway')){ + die("You must enable civicrm_spgateway module first before test."); + } + // check drupal version + $version = CRM_Core_Config::$_userSystem->version; + if($version < 8){ + $payment_page = variable_get('civicrm_demo_payment_page', array()); + } + else { + $payment_page = \Drupal::state()->get('civicrm_demo.payment_page'); + } + + $class_name = 'Payment_SPGATEWAY'; + if(isset($payment_page[$class_name])){ + $this->_page_id = $payment_page[$class_name]; + } + else { + $this->assertNotEmpty($this->_page_id, 'You need to have contribution page to procceed.'); + } + $this->_is_test = 1; // get processor @@ -137,7 +140,7 @@ function tearDownTest() { function testSinglePaymentNotify(){ $now = time(); - $trxn_id = 'singleut'.substr($now, -5); + $trxn_id = 'singleUt'.substr($now, -5); $amount = 111; // create contribution @@ -206,8 +209,7 @@ function testSinglePaymentNotify(){ 'Result' => $json, )); $post = array('JSONData' => $jsonData); - civicrm_spgateway_ipn('Credit', $post, $get); - + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); // verify contribution status after trigger $this->assertDBCompareValue( 'CRM_Contribute_DAO_Contribution', @@ -223,9 +225,146 @@ function testSinglePaymentNotify(){ $this->assertNotEmpty($cid, "In line " . __LINE__); } + function testSinglePaymentSync(){ + $now = time(); + $trxn_id = 'singleSyncUt'.substr($now, -5); + $amount = 200; + + // create contribution + $contrib = array( + 'trxn_id' => $trxn_id, + 'contact_id' => $this->_cid, + 'contribution_contact_id' => $this->_cid, + 'contribution_type_id' => 1, + 'contribution_page_id' => $this->_page_id, + 'payment_processor_id' => $this->_processor['id'], + 'payment_instrument_id' => 1, + 'created_date' => date('YmdHis', $now), + 'non_deductible_amount' => 0, + 'total_amount' => $amount, + 'currency' => 'TWD', + 'cancel_reason' => '0', + 'source' => 'AUTO: unit test', + 'contribution_source' => 'AUTO: unit test', + 'amount_level' => '', + 'is_test' => $this->_is_test, + 'is_pay_later' => 0, + 'contribution_status_id' => 4, + ); + + $contribution = CRM_Contribute_BAO_Contribution::create($contrib, CRM_Core_DAO::$_nullArray); + $this->assertNotEquals('CRM_Core_Error', get_class($contribution), "Contribution return error in line ".__LINE__.". Error messages:\n ".CRM_Core_Error::getMessages($contribution, "\n ")); + $this->assertNotEmpty($contribution->id, "In line " . __LINE__); + $params = array( + 'is_test' => $this->_is_test, + 'id' => $contribution->id, + ); + $this->assertDBState('CRM_Contribute_DAO_Contribution', $contribution->id, $params); + + // manually trigger ipn + $get = $post = $ids = array(); + $ids = CRM_Contribute_BAO_Contribution::buildIds($contribution->id); + $query = CRM_Contribute_BAO_Contribution::makeNotifyUrl($ids, NULL, $return_query = TRUE); + parse_str($query, $get); + $data = array( + 'MerchantID' => 'abcd',// have to modify + 'Amt' => $amount, + 'TradeNo' => '16112117153757079', + 'MerchantOrderNo' => $trxn_id, + 'RespondType' => 'JSON', + 'PaymentType' => 'CREDIT', + 'IP' => NULL, + 'EscrowBank' => 'KGI', + 'ItemDesc' => 'This is description.', + 'Gateway' => 'MPG', + 'IsLogin' => FALSE, + 'LangType' => 'zh-Tw', + 'PayTime' => date('Y-m-d H:i:s',$now), + 'RespondCode' => '00', + 'Exp' => '2112', + 'TokenUseStatus' => 0, + 'InstFirst' => 100, + 'InstEach' => 0, + 'Inst' => 0, + 'ECI' => '', + ); + $json = json_encode($data); + $jsonData = json_encode(array( + 'Status' => 'FAILED', + 'Message' => '授權失敗', + 'Result' => $json, + )); + $post = array('JSONData' => $jsonData); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); + + // verify contribution status after trigger + $this->assertDBCompareValue( + 'CRM_Contribute_DAO_Contribution', + $searchValue = $contribution->id, + $returnColumn = 'contribution_status_id', + $searchColumn = 'id', + $expectedValue = 4, + "In line " . __LINE__ + ); + + // verify data in drupal module + $cid = CRM_Core_DAO::singleValueQuery("SELECT cid FROM civicrm_contribution_spgateway WHERE cid = $contribution->id"); + $this->assertNotEmpty($cid, "In line " . __LINE__); + + //post data + $post = (object) array( + 'Status' => 'SUCCESS', + 'Message' => '查詢成功', + 'Result' => + (object)(array( + 'MerchantID' => 'abcd', + 'Amt' => $amount, + 'TradeNo' => '16112117153757079', + 'MerchantOrderNo' => $trxn_id, + 'TradeStatus' => '1', + 'PaymentType' => 'CREDIT', + 'CreateTime' => date('Y-m-d H:i:s'), + 'PayTime' => date('Y-m-d H:i:s'), + 'FundTime' => date('Y-m-d', $now + 86400*7), + 'RespondCode' => '00', + 'Auth' => '12345', + 'ECI' => NULL, + 'CloseAmt' => $amount, + 'CloseStatus' => '3', + 'BackBalance' => NULL, + 'BackStatus' => '0', + 'RespondMsg' => '授權成功', + 'Inst' => '0', + 'InstFirst' => '0', + 'InstEach' => '0', + 'PaymentMethod' => 'CREDIT', + )), + ); + try { + CRM_Core_Payment_SPGATEWAY::doSingleQueryRecord($contribution->id, $post); + } + catch (CRM_Core_Exception $e) { + $message = $e->getMessage(); + $code = $e->getErrorCode(); + if ($code != CRM_Core_Error::NO_ERROR) { + throw new Exception($message.' at line '.__LINE__, $code); + } + } + // verify contribution status after trigger + $this->assertDBCompareValue( + 'CRM_Contribute_DAO_Contribution', + $searchValue = $contribution->id, + $returnColumn = 'contribution_status_id', + $searchColumn = 'id', + $expectedValue = 1, + "In line " . __LINE__ + ); + + } + function testSingleWithWrongParms() { $now = time(); - $trxn_id = 'emptyut'.substr($now, -5); + $trxn_id = 'singleEmptyUt'.substr($now, -5); $amount = 222; // create contribution @@ -293,16 +432,15 @@ function testSingleWithWrongParms() { 'Result' => $json, )); $post = array('JSONData' => $jsonData); - $result = civicrm_spgateway_ipn('Credit', $post, $get); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); $error_msg = CRM_Core_DAO::singleValueQuery("SELECT note FROM civicrm_note WHERE entity_id = $contribution->id"); $this->assertNotEmpty($error_msg, "In line " . __LINE__); - $this->assertContains('Failuare', $error_msg, "In line " . __LINE__); - + $this->assertNotFalse(strpos($error_msg, 'Failuare')); } function testRecurringPaymentNotify(){ $now = time(); - $trxn_id = 'sput'.substr($now, -5); + $trxn_id = 'recurUt'.substr($now, -5); $amount = 222; // create recurring @@ -378,7 +516,8 @@ function testRecurringPaymentNotify(){ ); $instrument_code = $contrib['payment_instrument_id']; module_load_include('inc', 'civicrm_spgateway', 'civicrm_spgateway.checkout'); - $query = _civicrm_spgateway_notify_url($vars, 'spgateway/ipn/'.$instrument_code, 'contribute'); + $query = CRM_Contribute_BAO_Contribution::makeNotifyUrl($vars, 'spgateway/ipn/'.$instrument_code, TRUE); + parse_str($query, $get); $post = array( "Status" => "SUCCESS", @@ -402,8 +541,8 @@ function testRecurringPaymentNotify(){ $ppid = $this->_processor['id']; $get['ppid'] = $ppid; $PaymentProcessor = CRM_Core_BAO_PaymentProcessor::getPayment($ppid, $this->_is_test?'test':'live'); - $post = array('Period' => _civicrm_spgateway_recur_encrypt(json_encode($post), $PaymentProcessor)); - civicrm_spgateway_ipn('Credit', $post, $get); + $post = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt(json_encode($post), $PaymentProcessor)); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); // verify contribution status after trigger $this->assertDBCompareValue( @@ -460,8 +599,8 @@ function testRecurringPaymentNotify(){ "NextAuthDate" => "", ), ); - $post = array('Period' => _civicrm_spgateway_recur_encrypt(json_encode($post), $PaymentProcessor)); - civicrm_spgateway_ipn('Credit', $post, $get); + $post = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt(json_encode($post), $PaymentProcessor)); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); // $trxn_id2 = _civicrm_spgateway_recur_trxn($trxn_id, $gwsr1); // $trxn_id2 = "testdev500302T368_2"; @@ -521,8 +660,8 @@ function testRecurringPaymentNotify(){ "NextAuthDate" => "", ), ); - $post = array('Period' => _civicrm_spgateway_recur_encrypt(json_encode($post), $PaymentProcessor)); - civicrm_spgateway_ipn('Credit', $post, $get); + $post = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt(json_encode($post), $PaymentProcessor)); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); // $trxn_id2 = _civicrm_spgateway_recur_trxn($trxn_id, $gwsr1); // $trxn_id2 = "testdev500302T368_2"; @@ -579,7 +718,7 @@ function testRecurringPaymentNotify(){ )), ); - $result = civicrm_spgateway_single_check($trxn_id4, $create_contribution = TRUE, $post); + $result = CRM_Core_Payment_SPGATEWAY::recurSyncTransaction($trxn_id4, $create_contribution = TRUE, $post); $params = array( 1 => array($recurring->id, 'Integer'), ); @@ -624,9 +763,9 @@ function testRecurringPaymentNotify(){ "NextAuthDate" => "", ), ); - $post = array('Period' => _civicrm_spgateway_recur_encrypt(json_encode($post), $PaymentProcessor)); + $post = array('Period' => CRM_Core_Payment_SPGATEWAYAPI::recurEncrypt(json_encode($post), $PaymentProcessor)); - civicrm_spgateway_ipn('Credit', $post, $get); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); // $trxn_id2 = _civicrm_spgateway_recur_trxn($trxn_id, $gwsr1); // $trxn_id2 = "testdev500302T368_2"; @@ -657,7 +796,7 @@ function testRecurringPaymentNotify(){ function testNonCreditNotify(){ // BARCODE : 11 $now = time()+300; - $trxn_id = 'ut'.substr($now, -5); + $trxn_id = 'nonCreditUt'.substr($now, -5); $amount = 111; $instrument_id = 11; @@ -680,7 +819,7 @@ function testNonCreditNotify(){ // CVS : 12 $now = $now + 60; - $trxn_id = 'ut'.substr($now, -5); + $trxn_id = 'cvsUt'.substr($now, -5); $amount = 111; $instrument_id = 12; @@ -701,7 +840,7 @@ function testNonCreditNotify(){ // ATM : 14 $now = $now + 60; - $trxn_id = 'ut'.substr($now, -5); + $trxn_id = 'atmUt'.substr($now, -5); $amount = 111; $instrument_id = 14; @@ -735,7 +874,7 @@ function testNonCreditNotify(){ 'TEST2' => 'BBB', ); $_GET['q'] = 'spgateway/record'; - civicrm_spgateway_record($cid); + CRM_Core_Payment_SPGATEWAYAPI::writeRecord($cid); $this->assertDBQuery($cid, "SELECT cid FROM civicrm_contribution_spgateway WHERE data LIKE '%#info%TEST1%' AND cid = $cid"); */ } @@ -793,7 +932,7 @@ function doSingleNonCreditTest($now, $trxn_id, $amount, $instrument_id, $payment 'Result' => $json, )); $post = array('JSONData' => $jsonData); - civicrm_spgateway_ipn('Credit', $post, $get); + $this->doIPN(array('spgateway', 'ipn', 'Credit'), $post, $get, __LINE__); // verify contribution status after trigger $this->assertDBCompareValue( @@ -810,5 +949,18 @@ function doSingleNonCreditTest($now, $trxn_id, $amount, $instrument_id, $payment $this->assertNotEmpty($cid, "In line " . __LINE__); } + function doIPN($args, $post, $get, $line) { + try { + CRM_Core_Payment_SPGATEWAY::doIPN($args, $post, $get); + } + catch (CRM_Core_Exception $e) { + $message = $e->getMessage(); + $data = $e->getErrorData(); + $code = $e->getErrorCode(); + if ($code != CRM_Core_Error::NO_ERROR) { + throw new Exception($message.' at line '.$line, $code); + } + } + } } diff --git a/tests/phpunit/CiviTest/truncate.xml b/tests/phpunit/CiviTest/truncate.xml index c8f7c0d61..ce87e1dc9 100644 --- a/tests/phpunit/CiviTest/truncate.xml +++ b/tests/phpunit/CiviTest/truncate.xml @@ -287,6 +287,8 @@ + + diff --git a/tests/playwright/tests/add_membership_type.spec.js b/tests/playwright/tests/add_membership_type.spec.js index 4d18547d9..67a28462d 100644 --- a/tests/playwright/tests/add_membership_type.spec.js +++ b/tests/playwright/tests/add_membership_type.spec.js @@ -18,6 +18,7 @@ test.afterAll(async () => { test.describe.serial('Create Membership Type', () => { var organization = utils.makeid(10); var membership_type = 'typeForTest'; + var element; test('Create Organization Contact', async () => { await page.goto('civicrm/contact/add?reset=1&ct=Organization'); await utils.wait(wait_secs); @@ -46,5 +47,38 @@ test.describe.serial('Create Membership Type', () => { await page.locator('[id="_qf_MembershipType_upload-bottom"]').click(); await expect(page.getByRole('cell', { name: membership_type })).toHaveText(membership_type); }); + test('Create Membership', async ()=> { + var firstName = 'test_firstName'; + var lastName = 'test_lastName'; + var name = firstName + ' ' + lastName; + // go to create membership page + await page.goto('/civicrm/member/add?reset=1&action=add&context=standalone'); + + // create individual data + await page.locator('#profiles_1').selectOption('4'); + await expect(page.getByRole('dialog')).toBeVisible(); + element = await utils.findElementByLabel(page, '名字\n *'); + await utils.fillInput(element, firstName); + element = await utils.findElementByLabel(page, '姓氏\n *'); + await utils.fillInput(element, lastName); + await page.locator('#_qf_Edit_next').click(); + await expect(page.locator('#contact_1')).toHaveValue(name); + + // select the first option in the membership type and orginization + element = page.locator('[id="membership_type_id\\[0\\]"]'); + // await utils.selectOption(page.locator(element), { index: 0 }); + await element.selectOption(organization); + element = page.locator('[id="membership_type_id\\[1\\]"]'); + await element.selectOption(membership_type); + // pick the first date + await page.locator('#join_date').click(); + await page.getByRole('link', { name: '1', exact: true }).click(); + await page.locator('#start_date').click(); + await page.getByRole('link', { name: '1', exact: true }).click(); + await page.locator('[id="_qf_Membership_upload-bottom"]').click(); + // await utils.wait(wait_secs); + await expect(page).toHaveTitle(name + ' | netiCRM'); + await expect(page.locator('#option11>tbody')).toContainText(membership_type); + }); }); \ No newline at end of file diff --git a/tests/playwright/tests/contribution_spgateway.spec.js b/tests/playwright/tests/contribution_spgateway.spec.js index 9ece77d11..b55a45fed 100644 --- a/tests/playwright/tests/contribution_spgateway.spec.js +++ b/tests/playwright/tests/contribution_spgateway.spec.js @@ -7,7 +7,7 @@ let element; var vars = { path: 'civicrm/contribute/transact', - query: 'reset=1&action=preview&id=3', + query: 'reset=1&action=preview&id=2', site_name: 'netiCRM', // you should add your own testing variables below @@ -58,7 +58,7 @@ test.describe.serial('SPGATEWAY', () => { await utils.fillInput(page.locator(element), vars.amount); /* choose "SPGATEWAY" as the "Payment Method" */ - element = '#CIVICRM_QFID_8_2'; + element = '#CIVICRM_QFID_6_2'; await utils.findElement(page, element); await utils.clickElement(page, page.locator(element)); await expect(page.locator(element)).toBeChecked(); diff --git a/tests/playwright/tests/report_check.spec.js b/tests/playwright/tests/report_check.spec.js new file mode 100644 index 000000000..537993c54 --- /dev/null +++ b/tests/playwright/tests/report_check.spec.js @@ -0,0 +1,69 @@ +const { test, expect, chromium } = require('@playwright/test'); +const utils = require('./utils.js'); + +/** @type {import('@playwright/test').Page} */ +let page; +const wait_secs = 2000; +const site_name = 'netiCRM'; + +test.beforeAll(async () => { + const browser = await chromium.launch(); + page = await browser.newPage(); +}); + +test.afterAll(async () => { + await page.close(); +}); + + +test.describe.serial('Report Check', () => { + test('Check report pages', async() => { + var num_url; + var url_locator, url_text, report_content_locator; + var err_message_locator, err_message; + + // capture the number of urls requires to check + await page.goto('/civicrm/report/list?reset=1'); + await expect(page.locator('#crm-container>div>div>div.crm-accordion-header').first()).toContainText('Contact Report'); + url_locator = 'table.report-layout>tbody>tr'; + num_url = await page.locator(url_locator).count(); + console.log('There are %d urls that requires to check.', num_url); + // start checking + for (let i=1; i<=num_url; i++){ + await page.goto('/civicrm/report/list?reset=1'); + await expect(page.locator('#crm-container>div>div>div.crm-accordion-header').first()).toContainText('Contact Report'); + // click the url button + url_locator = `#row_${i}>td>a>strong`; + url_text = await page.locator(url_locator).textContent(); + await page.locator(url_locator).click(); + await utils.wait(wait_secs); + let full_title = url_text + ' | ' + site_name; + await expect(page).toHaveTitle(full_title); + console.log('Enter ', full_title , ' page.'); + + // check page content + report_content_locator = '#crm-container>form>div>div>div>div.crm-accordion-header'; + await expect(page.locator(report_content_locator)).not.toHaveCount(0); + + // check error message and print button + err_message_locator = '#crm-container>form>div>div.messages.status'; + err_message = await page.locator(err_message_locator).count(); + if (err_message != 0){ + // error message + console.log('%d. there is an error message, skip.', i); + continue; + } + else{ + // no error message and press print button + console.log('%d. there is no error message.', i); + await page.getByText('Print Report', {exact: true}).click(); + if (i==17){ + // handle a special case + await expect(page.locator('#crm-container>div>h1')).toHaveText('Membership Detail Report'); + }else{ + await expect(page.locator('#crm-container>div>h1')).toHaveText(url_text); + } + } + } + }); +}); \ No newline at end of file diff --git a/tests/playwright/tests/version_check.spec.js b/tests/playwright/tests/version_check.spec.js index 4f8e36554..1c09b2953 100644 --- a/tests/playwright/tests/version_check.spec.js +++ b/tests/playwright/tests/version_check.spec.js @@ -13,30 +13,99 @@ test.beforeAll(async () => { const browser = await chromium.launch(); page = await browser.newPage(); }); - test.afterAll(async () => { await page.close(); }); test.describe.serial('Version Check', () => { test.use({ storageState: 'storageState.json' }); test('Check Pages', async () => { + // read checking urls in site file const rl = readline.createInterface({ input: fs.createReadStream('tests/files/sites.csv'), crlfDelay: Infinity }); for await (const line of rl) { + //look for any extra commas at the beginning (^) or end ($) of the toy (line), and get rid of them. vars.urls.push(line.replace(/^,+|,+$/g, '')); } + var numStation = 1; + // create new empty file + fs.open('./tests/files/check_site_result.txt', 'w', function (err, file) { + if (err) throw err; + console.log('New output is created!'); + }); for (const url of vars.urls) { - const userResponse = await page.goto('https://' + url + '/user'); - await utils.wait(wait_secs); - await expect(userResponse.status()).toBe(200); - await utils.print(`- ${await page.title()} -> Ok`); - const mailingResponse = await page.goto('https://' + url + '/civicrm/mailing/subscribe?reset=1'); - await utils.wait(wait_secs); - await expect(mailingResponse.status()).toBe(200); - await utils.findElement(page, '#email'); - await utils.print(`- ${await page.title()} -> Ok`); + // append current site to file + fs.appendFile('./tests/files/check_site_result.txt', '\n' + numStation + ' ' + url + ' START\n', function(err){ + if (err) console.log(err); + else console.log('\n' + numStation + ' ' + url + ' START'); + }) + // check user path + try{ + const userResponse = await page.goto('https://' + url + '/user'); + const userApiStatus = await userResponse.status(); + var result; + if ( userApiStatus ==200){ + await expect(userApiStatus).toBe(200); + // check error message + var error = await page.locator('.crm-error').count(); + if (error == 0){ + await expect(page.locator('.crm-error')).toHaveCount(0); + // check user login element + var loginElement = await page.getByText('密碼').first().isVisible(); + if (!loginElement){ + result = numStation + ' ' + url + '/user PASS\n'; + }else{ + result = numStation + ' ' + url + '/user FAIL Reason:There is no user login element\n'; + } + }else{ + // have error message + result = numStation + ' ' + url + '/user FAIL Reason:An error message appears\n'; + } + }else{ + result = url + '/user FAIL Reason:The status is ' + userApiStatus +'\n'; + } + }catch (error){ + result = numStation + ' ' + url + '/user FAIL'+ '\n' + 'error message: ' + error.message +'\n'; + } + // append user path status result to file + fs.appendFile('./tests/files/check_site_result.txt', result, function(err){ + if (err) console.log(err); + else console.log(numStation + ' Append opertaion complete for user path.'); + }) + // check subscribe path status + try{ + const mailingResponse = await page.goto('https://' + url + '/civicrm/mailing/subscribe?reset=1'); + const mailApiStatus = await mailingResponse.status(); + if ( mailApiStatus ==200){ + await expect(mailApiStatus).toBe(200); + // check error message + var error = await page.locator('.crm-error').count(); + if (error == 0){ + await expect(page.locator('.crm-error')).toHaveCount(0); + // check user login element + var loginElement = await page.locator('#Subscribe').isVisible(); + if (loginElement){ + result = numStation + ' ' + url + '/subscribe PASS\n'; + }else{ + result = numStation + ' ' + url + '/subscribe FAIL Reason:There is no subscribe element\n'; + } + }else{ + // have error message + result = numStation + ' ' + url + '/subscribe FAIL Reason:An error message appears\n'; + } + }else{ + result = url + '/subscribe FAIL Reason:The status is ' + userApiStatus +'\n'; + } + }catch (error){ + result = numStation + ' ' + url + '/subscribe FAIL'+ '\n' + 'error message: ' + error.message + '\n'; + } + // append subscribe path status result to file + fs.appendFile('./tests/files/check_site_result.txt', result, function(err){ + if (err) console.log(err); + else console.log(numStation + ' Append opertaion complete for subscribe path.'); + }) + numStation++; } }); }); \ No newline at end of file diff --git a/tools/scripts/release-neticrm.sh b/tools/scripts/release-neticrm.sh index 929a98994..d6b8ee6b6 100755 --- a/tools/scripts/release-neticrm.sh +++ b/tools/scripts/release-neticrm.sh @@ -2,7 +2,7 @@ CALLEDPATH=`dirname $0` CIVICRMPATH=`cd $CALLEDPATH/../../ && pwd` LANGUAGE='zh_TW' -MAJOR_VERSION='6.0' +MAJOR_VERSION='6.1' neticrm_merge(){ TAG=`git tag | grep "^$MAJOR_VERSION" | awk -F "." '{print $3}' | sort -nr | head -n 1` diff --git a/xml/schema/Contribute/ContributionSpgateway.xml b/xml/schema/Contribute/ContributionSpgateway.xml new file mode 100644 index 000000000..e28f91d61 --- /dev/null +++ b/xml/schema/Contribute/ContributionSpgateway.xml @@ -0,0 +1,46 @@ + + + + CRM/Contribute + SPGATEWAY + civicrm_contribution_spgateway + Spgateway and neweb data warehouse + 4.0 + + id + int unsigned + true + 4.0 + + + id + true + + + cid + spgateway_contribution_id + int unsigned + Spgateway Contribution ID + FK to contribution table + 4.0 + + + cid +
civicrm_contribution
+ id + 2.0 + SET NULL + + + UI_spgateway_id + cid + true + 4.0 + + + data + Spgateway return data + blob + 4.0 + + diff --git a/xml/schema/Contribute/files.xml b/xml/schema/Contribute/files.xml index 9b04f969e..9e6bed964 100644 --- a/xml/schema/Contribute/files.xml +++ b/xml/schema/Contribute/files.xml @@ -11,6 +11,7 @@ +