RedsysIPN.php 8.05 KB
Newer Older
sluc23's avatar
sluc23 committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

class CRM_Core_Payment_RedsysIPN extends CRM_Core_Payment_BaseIPN {

  CONST REDSYS_RESPONSE_CODE_ACCEPTED = '0000';

  private $_errors;
  private $_redsysAPI;

  function __construct() {
    parent::__construct();

    $this->_redsysAPI  = new RedsysAPI;
    $this->_errors = array(
      "101"  => "Tarjeta caducada",
      "102"  => "Tarjeta en excepción transitoria o bajo sospecha de fraude",
      "106"  => "Intentos de PIN excedidos",
      "125"  => "Tarjeta no efectiva",
      "129"  => "Código de seguridad (CVV2/CVC2) incorrecto",
      "180"  => "Tarjeta ajena al servicio",
      "184"  => "Error en la autenticación del titular",
      "190"  => "Denegación del emisor sin especificar motivo",
      "191"  => "Fecha de caducidad errónea",
      "202"  => "Tarjeta en excepción transitoria o bajo sospecha de fraude con retirada de tarjeta",
      "904"  => "Comercio no registrado en FUC",
      "909"  => "Error de sistema",
      "913"  => "Pedido repetido",
      "944"  => "Sesión Incorrecta",
      "950"  => "Operación de devolución no permitida",
      "912"  => "Emisor no disponible",
      "9912" => "Emisor no disponible",
      "9064" => "Número de posiciones de la tarjeta incorrecto",
      "9078" => "Tipo de operación no permitida para esa tarjeta",
      "9093" => "Tarjeta no existente",
      "9094" => "Rechazo servidores internacionales",
      "9104" => "Comercio con “titular seguro” y titular sin clave de compra segura",
      "9218" => "El comercio no permite op. seguras por entrada /operaciones",
      "9253" => "Tarjeta no cumple el check-digit",
      "9256" => "El comercio no puede realizar preautorizaciones",
      "9257" => "Esta tarjeta no permite operativa de preautorizaciones",
      "9261" => "Operación detenida por superar el control de restricciones en la entrada al SIS",
Carlos Capote's avatar
Carlos Capote committed
42
43
      "9913" => "Error en la confirmación que el comercio envía al TPV Virtual (solo aplicable en la opción de sincronización SOAP)",
      "9914" => "Confirmación “KO” del comercio (solo aplicable en la opción de sincronización SOAP)",
sluc23's avatar
sluc23 committed
44
      "9915" => "A petición del usuario se ha cancelado el pago",
Carlos Capote's avatar
Carlos Capote committed
45
      "9928" => "Anulación de autorización en diferido realizada por el SIS (proceso batch)",
sluc23's avatar
sluc23 committed
46
47
48
49
50
51
52
53
54
55
56
      "9929" => "Anulación de autorización en diferido realizada por el comercio",
      "9997" => "Se está procesando otra transacción en SIS con la misma tarjeta",
      "9998" => "Operación en proceso de solicitud de datos de tarjeta",
      "9999" => "Operación que ha sido redirigida al emisor a autenticar",
    );
  }

  function single(&$input, &$ids, &$objects, $recur = FALSE, $first = FALSE) {
    $contribution = &$objects['contribution'];

    if (!$recur) {
57
      if (str_replace(",", "", $contribution->total_amount) != str_replace(",", "", $input['amount'])) {
sluc23's avatar
sluc23 committed
58
59
60
61
62
63
64
65
66
        CRM_Core_Error::debug_log_message("Amount values dont match between database and IPN request");
        echo "Failure: Amount values dont match between database and IPN request<p>";
        return FALSE;
      }
    }

    $transaction = new CRM_Core_Transaction();
    if ($input['Ds_Response'] != self::REDSYS_RESPONSE_CODE_ACCEPTED) {
      $error = self::trimAmount($input['Ds_Response']);
67
      if (array_key_exists($error, $this->_errors)) {
sluc23's avatar
sluc23 committed
68
        $input['reasonCode'] = $this->_errors[$error];
69
70
      }
      else {
71
72
73
74
        $input['reasonCode'] = $error;
      }
      CRM_Core_Error::debug_log_message("Redsys IPN Response: About to cancel contr \n input: " . print_r($input, TRUE) . "\n ids: " . print_r($ids, TRUE) . "\n objects: " . print_r($objects, TRUE));
      try {
rubofvil's avatar
rubofvil committed
75
        civicrm_api3('contribution', 'create', array('id' => $input['contributionID'], 'contribution_status_id' => 'Cancelled', 'cancel_reason' => $input['reasonCode'], 'cancel_date' => date('Y-m-d')));
76
77
78
79
80
      }
      catch (CiviCRM_API3_Exception $e) {
        if($e->getMessage()) {
          CRM_Core_Error::debug_log_message("Redsys IPN Error Updating contribution: " . $e->getMessage());
        }
sluc23's avatar
sluc23 committed
81
      }
rubofvil's avatar
rubofvil committed
82
      return TRUE;
sluc23's avatar
sluc23 committed
83
84
    }

85
    CRM_Core_Error::debug_log_message("Redsys IPN Response: About complete trans \n input: " . print_r($input, TRUE) . "\n ids: " . print_r($ids, TRUE) . "\n objects: " . print_r($objects, TRUE));
86
87
88
89
90
91
92
93
94
    try {
      civicrm_api3('contribution', 'completetransaction', array('id' => $input['contributionID'], 'trxn_id' => $input["trxn_id"]));
    }
    catch (CiviCRM_API3_Exception $e) {
      if (!stristr($e->getMessage(), 'Contribution already completed')) {
        CRM_Core_Error::debug_log_message("Redsys IPN Error Updating contribution: " . $e->getMessage());
      }
    }
    return TRUE;
sluc23's avatar
sluc23 committed
95
96
97
98
  }

  function getInput(&$input, &$ids) {
    $input = array(
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
      // GET Parameters.
      'module' => self::retrieve('md', 'String', 'GET', TRUE),
      'component' => self::retrieve('md', 'String', 'GET', TRUE),
      'qfKey' => self::retrieve('qfKey', 'String', 'GET', FALSE),
      'contributionID' => self::retrieve('contributionID', 'String', 'GET', TRUE),
      'contactID' => self::retrieve('contactID', 'String', 'GET', TRUE),
      'eventID' => self::retrieve('eventID', 'String', 'GET', FALSE),
      'participantID' => self::retrieve('participantID', 'String', 'GET', FALSE),
      'membershipID' => self::retrieve('membershipID', 'String', 'GET', FALSE),
      'contributionPageID' => self::retrieve('contributionPageID', 'String', 'GET', FALSE),
      'relatedContactID' => self::retrieve('relatedContactID', 'String', 'GET', FALSE),
      'onBehalfDupeAlert' => self::retrieve('onBehalfDupeAlert', 'String', 'GET', FALSE),
      // POST Parameters.
      'Ds_SignatureVersion' => self::retrieve('Ds_SignatureVersion', 'String', 'POST', TRUE),
      'Ds_MerchantParameters' => self::retrieve('Ds_MerchantParameters', 'String', 'POST', TRUE),
      'Ds_Signature' => self::retrieve('Ds_Signature', 'String', 'POST', TRUE),
sluc23's avatar
sluc23 committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
    );
    $decodecResponseJson           = $this->_redsysAPI->decodeMerchantParameters($input["Ds_MerchantParameters"]);
    $decodecResponse               = json_decode($decodecResponseJson);
    $input['Ds_MerchantCode']      = $decodecResponse->Ds_MerchantCode;
    $input['Ds_Response']          = $decodecResponse->Ds_Response;
    $input['Ds_AuthorisationCode'] = $decodecResponse->Ds_AuthorisationCode;
    $input['Ds_Amount']            = $decodecResponse->Ds_Amount;
    $input['amount']               = number_format(($decodecResponse->Ds_Amount / 100), 2);
    $input['trxn_id']              = $decodecResponse->Ds_AuthorisationCode;

    $ids = array(
      'contribution'  => $input['contributionID'],
      'contact'       => $input['contactID'],
    );
    if ($input['module'] == "event") {
      $ids['event']       = $input['eventID'];
      $ids['participant'] = $input['participantID'];
    }
    else {
      $ids['membership']          = $input['membershipID'];
      $ids['related_contact']     = $input['relatedContactID'];
      $ids['onbehalf_dupe_alert'] = $input['onBehalfDupeAlert'];
    }
  }

  function validateData($paymentProcessor, &$input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) {
    $signatureNotif = $this->_redsysAPI->createMerchantSignatureNotif($paymentProcessor["password"], $input["Ds_MerchantParameters"]);

143
    if ($input['Ds_MerchantCode'] != $paymentProcessor["user_name"]) {
sluc23's avatar
sluc23 committed
144
      CRM_Core_Error::debug_log_message("Redsys Response param Ds_MerchantCode incorrect");
145
      return FALSE;
sluc23's avatar
sluc23 committed
146
147
    }

148
    if ($signatureNotif !== $input['Ds_Signature']) {
sluc23's avatar
sluc23 committed
149
      CRM_Core_Error::debug_log_message("Redsys signature doesn't match");
150
      return FALSE;
sluc23's avatar
sluc23 committed
151
152
153
154
155
    }

    return parent::validateData($input, $ids, $objects, $required, $paymentProcessorID);
  }

156
157
158
159
  static function retrieve($name, $type, $location = 'POST', $abort = TRUE) {
    static $store = NULL;
    $value = CRM_Utils_Request::retrieve($name, $type, $store, FALSE, NULL, $location);
    if ($abort && $value === NULL) {
sluc23's avatar
sluc23 committed
160
161
162
163
164
165
166
      CRM_Core_Error::debug_log_message("Could not find an entry for $name in $location");
      echo "Failure: Missing Parameter<p>";
      exit();
    }
    return $value;
  }

167
  static function trimAmount($amount, $pad = '0') {
sluc23's avatar
sluc23 committed
168
169
170
171
    return ltrim(trim($amount), $pad);
  }

}