Index: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect.adapter.php |
— | — | @@ -1085,6 +1085,7 @@ |
1086 | 1086 | $cancelflag = false; //this will denote the thing we're trying to do with the donation attempt |
1087 | 1087 | $problemflag = false; //this will get set to true, if we can't continue and need to give up and just log the hell out of it. |
1088 | 1088 | $problemmessage = ''; //to be used in conjunction with the flag. |
| 1089 | + $deletelimbomessageflag = false; //this tells us if we should delete this transaction's limbo queue message or not. |
1089 | 1090 | |
1090 | 1091 | |
1091 | 1092 | if ( $post_status_check ){ |
— | — | @@ -1131,11 +1132,13 @@ |
1132 | 1133 | switch ( $order_status_results ){ |
1133 | 1134 | case 'failed' : |
1134 | 1135 | case 'revised' : |
| 1136 | + $deletelimbomessageflag = true; |
1135 | 1137 | $cancelflag = true; //makes sure we don't try to confirm. |
1136 | 1138 | break; |
1137 | 1139 | case 'complete' : |
1138 | 1140 | $problemflag = true; //nothing to be done. |
1139 | 1141 | $problemmessage = "GET_ORDERSTATUS reports that the payment is already complete."; |
| 1142 | + $deletelimbomessageflag = true; |
1140 | 1143 | break; |
1141 | 1144 | } |
1142 | 1145 | } |
— | — | @@ -1167,6 +1170,7 @@ |
1168 | 1171 | $this->setTransactionResult( "Original Response Status (pre-SET_PAYMENT): " . $original_status_code, 'txn_message' ); |
1169 | 1172 | $this->runPostProcessHooks(); //stomp is in here |
1170 | 1173 | $this->unsetAllSessionData(); |
| 1174 | + $deletelimbomessageflag = true; |
1171 | 1175 | } else { |
1172 | 1176 | $problemflag = true; |
1173 | 1177 | $problemmessage = "SET_PAYMENT couldn't communicate properly!"; |
— | — | @@ -1178,17 +1182,36 @@ |
1179 | 1183 | if ( isset( $final['status'] ) && $final['status'] === true ) { |
1180 | 1184 | $this->setTransactionWMFStatus( 'failed' ); |
1181 | 1185 | $this->unsetAllSessionData(); |
| 1186 | + $deletelimbomessageflag = true; |
1182 | 1187 | } else { |
1183 | 1188 | $problemflag = true; |
1184 | 1189 | $problemmessage = "CANCEL_PAYMENT couldn't communicate properly!"; |
1185 | 1190 | } |
1186 | 1191 | } |
1187 | | - //No else. We can't be in here if we've had problems, so the |
1188 | | - //GET_STATUS must have told us no. No action required (in fact, |
1189 | | - //GC will complain if we try to can something at this point). |
| 1192 | + //No else. We can't be in here if we've had problems, so the |
| 1193 | + //GET_STATUS must have told us no. No action required (in fact, |
| 1194 | + //GC will complain if we try to can something at this point). |
1190 | 1195 | } |
1191 | 1196 | } |
1192 | 1197 | |
| 1198 | + if ( $deletelimbomessageflag ) { |
| 1199 | + //ack the message out of the limbo queue. |
| 1200 | + |
| 1201 | + //TODO: Test this in a batch situation. I have reason to suspect that the selectors won't work if the stomp connection isn't being reset properly. |
| 1202 | + $limbo_messages = stompFetchMessages( 'limbo', "JMSCorrelationID = '" . $this->getCorrelationID() . "'" ); |
| 1203 | + $msgCount = count($limbo_messages); |
| 1204 | + if ($msgCount){ |
| 1205 | + stompAckMessages($limbo_messages); |
| 1206 | + if ($msgCount > 1){ |
| 1207 | + self::log($this->getData_Raw( 'contribution_tracking_id' ) . ':' . $this->getData_Raw( 'order_id' ) . " - Deleted $msgCount limbo messages."); |
| 1208 | + } |
| 1209 | + } else { |
| 1210 | + self::log($this->getData_Raw( 'contribution_tracking_id' ) . ':' . $this->getData_Raw( 'order_id' ) . " - No limbo messages found."); |
| 1211 | + } |
| 1212 | + |
| 1213 | + closeDIStompConnection(); |
| 1214 | + } |
| 1215 | + |
1193 | 1216 | if ( $problemflag ){ |
1194 | 1217 | //we have probably had a communication problem that could mean stranded payments. |
1195 | 1218 | $problemmessage = $this->getData_Raw( 'contribution_tracking_id' ) . ':' . $this->getData_Raw( 'order_id' ) . ' ' . $problemmessage; |
— | — | @@ -1286,8 +1309,6 @@ |
1287 | 1310 | |
1288 | 1311 | $transaction = $this->getCurrentTransaction(); |
1289 | 1312 | |
1290 | | - $this->getTransactionStatus(); |
1291 | | - |
1292 | 1313 | switch ( $transaction ) { |
1293 | 1314 | case 'INSERT_ORDERWITHPAYMENT': |
1294 | 1315 | $data = $this->xmlChildrenToArray( $response, 'ROW' ); |
— | — | @@ -2015,9 +2036,19 @@ |
2016 | 2037 | } |
2017 | 2038 | } |
2018 | 2039 | |
| 2040 | + /** |
| 2041 | + * post-process function for INSERT_ORDERWITHPAYMENT. |
| 2042 | + * This gets called by executeIfFunctionExists, in do_transaction. |
| 2043 | + */ |
2019 | 2044 | protected function post_process_insert_orderwithpayment(){ |
2020 | 2045 | //yeah, we absolutely want to do this for every one of these. |
2021 | | - $this->doLimboStompTransaction(); |
| 2046 | + if ( $this->getTransactionStatus() === true ) { |
| 2047 | + $data = $this->getTransactionData(); |
| 2048 | + $action = $this->findCodeAction( 'GET_ORDERSTATUS', 'STATUSID', $data['STATUSID'] ); |
| 2049 | + if ($action != 'failed'){ |
| 2050 | + $this->doLimboStompTransaction(); |
| 2051 | + } |
| 2052 | + } |
2022 | 2053 | } |
2023 | 2054 | |
2024 | 2055 | protected function pre_process_get_orderstatus(){ |
— | — | @@ -2059,39 +2090,4 @@ |
2060 | 2091 | return $result; |
2061 | 2092 | } |
2062 | 2093 | |
2063 | | - /** |
2064 | | - * Function that adds a stomp message to a special 'limbo' queue, for data |
2065 | | - * that is either highly likely or completely guaranteed to be bifurcated by |
2066 | | - * handing the ball to a third-party process. |
2067 | | - * No need to override doStompTransaction in the parent class, as that has |
2068 | | - * more logic than we need here. However, we may consider functionalizing |
2069 | | - * some of the copied code in the parent class. |
2070 | | - * @return void |
2071 | | - */ |
2072 | | - protected function doLimboStompTransaction() { |
2073 | | - if ( !$this->getGlobal( 'EnableStomp' ) ){ |
2074 | | - return; |
2075 | | - } |
2076 | | - $this->debugarray[] = "Attempting Limbo Stomp Transaction!"; |
2077 | | - $hook = 'gwLimboStomp'; |
2078 | | - |
2079 | | - // send the thing. |
2080 | | - $transaction = array( |
2081 | | - 'response' => $this->getTransactionMessage(), |
2082 | | - 'date' => time(), |
2083 | | - 'gateway_txn_id' => $this->getTransactionGatewayTxnID(), |
2084 | | - 'correlation-id' => 'GC-' . $this->getData_Raw('order_id'), |
2085 | | - ); |
2086 | | - |
2087 | | - $stomp_fields = $this->dataObj->getStompMessageFields(); |
2088 | | - foreach ($stomp_fields as $field){ |
2089 | | - $transaction[$field] = $this->getData_Raw($field); |
2090 | | - } |
2091 | | - |
2092 | | - try { |
2093 | | - wfRunHooks( $hook, array( $transaction ) ); |
2094 | | - } catch ( Exception $e ) { |
2095 | | - self::log( "STOMP ERROR. Could not add message. " . $e->getMessage() , LOG_CRIT ); |
2096 | | - } |
2097 | | - } |
2098 | 2094 | } |
Index: trunk/extensions/DonationInterface/gateway_common/gateway.adapter.php |
— | — | @@ -1512,7 +1512,52 @@ |
1513 | 1513 | self::log( "STOMP ERROR. Could not add message. " . $e->getMessage() , LOG_CRIT ); |
1514 | 1514 | } |
1515 | 1515 | } |
| 1516 | + |
| 1517 | + |
| 1518 | + /** |
| 1519 | + * Function that adds a stomp message to a special 'limbo' queue, for data |
| 1520 | + * that is either highly likely or completely guaranteed to be bifurcated by |
| 1521 | + * handing the ball to a third-party process. |
| 1522 | + * TODO: Functionalize some of the code copied from doStompTransaction. |
| 1523 | + * @return null |
| 1524 | + */ |
| 1525 | + protected function doLimboStompTransaction() { |
| 1526 | + if ( !$this->getGlobal( 'EnableStomp' ) ){ |
| 1527 | + return; |
| 1528 | + } |
| 1529 | + $this->debugarray[] = "Attempting Limbo Stomp Transaction!"; |
| 1530 | + $hook = 'gwLimboStomp'; |
1516 | 1531 | |
| 1532 | + $stomp_fields = $this->dataObj->getStompMessageFields(); |
| 1533 | + |
| 1534 | + $transaction = array( |
| 1535 | + 'response' => $this->getTransactionMessage(), |
| 1536 | + 'date' => time(), |
| 1537 | + 'gateway_txn_id' => $this->getTransactionGatewayTxnID(), |
| 1538 | + 'correlation-id' => $this->getCorrelationID(), |
| 1539 | + 'payment_method' => $this->getData_Raw('payment_method') |
| 1540 | + ); |
| 1541 | + |
| 1542 | + $raw_data = array(); |
| 1543 | + foreach ($stomp_fields as $field){ |
| 1544 | + if (!isset($transaction[$field])){ |
| 1545 | + $raw_data[$field] = $this->getData_Raw($field); |
| 1546 | + } |
| 1547 | + } |
| 1548 | + |
| 1549 | + $transaction = array_merge($transaction, $raw_data); |
| 1550 | + |
| 1551 | + try { |
| 1552 | + wfRunHooks( $hook, array( $transaction ) ); |
| 1553 | + } catch ( Exception $e ) { |
| 1554 | + self::log( "STOMP ERROR. Could not add message. " . $e->getMessage() , LOG_CRIT ); |
| 1555 | + } |
| 1556 | + } |
| 1557 | + |
| 1558 | + protected function getCorrelationID(){ |
| 1559 | + return $this->getIdentifier() . '-' . $this->getData_Raw('order_id'); |
| 1560 | + } |
| 1561 | + |
1517 | 1562 | function smooshVarsForStaging() { |
1518 | 1563 | |
1519 | 1564 | foreach ( $this->staged_vars as $field ) { |
Index: trunk/extensions/DonationInterface/activemq_stomp/activemq_stomp.php |
— | — | @@ -142,9 +142,15 @@ |
143 | 143 | |
144 | 144 | // connect |
145 | 145 | $con->connect(); |
| 146 | + |
| 147 | + $properties = array( |
| 148 | + 'persistent' => 'true', |
| 149 | + 'correlation-id' => $transaction['correlation-id'], |
| 150 | + 'payment_method' => $transaction['payment_method'] |
| 151 | + ); |
146 | 152 | |
147 | 153 | // send a message to the queue |
148 | | - $result = $con->send( "/queue/$queueName", $message, array( 'persistent' => 'true', 'correlation-id' => $transaction['correlation-id'] ) ); |
| 154 | + $result = $con->send( "/queue/$queueName", $message, $properties ); |
149 | 155 | |
150 | 156 | if ( !$result ) { |
151 | 157 | wfDebugLog( 'activemq_stomp', 'Send to Q failed for this message: ' . $message ); |
— | — | @@ -218,3 +224,86 @@ |
219 | 225 | |
220 | 226 | return $message; |
221 | 227 | } |
| 228 | + |
| 229 | +/** |
| 230 | + * Fetches all the messages in a queue that match the supplies selector. |
| 231 | + * Limiting to a completely arbitrary 50, just in case something goes amiss somewhere. |
| 232 | + * @param string $queue The target queue from which we would like to fetch things. |
| 233 | + * To simplify things, specify either 'verified', 'pending', or 'limbo'. |
| 234 | + * @param string $selector Could be anything that STOMP will regard as a valid selector. For our purposes, we will probably do things like: |
| 235 | + * $selector = "JMSCorrelationID = 'globalcollect-6214814668'", or |
| 236 | + * $selector = "payment_method = 'cc'"; |
| 237 | + * @param int $limit The maximum number of messages we would like to pull off of the queue at one time. |
| 238 | + * @return array an array of stomp messages, with a count of up to $limit. |
| 239 | + */ |
| 240 | +function stompFetchMessages( $queue, $selector = null, $limit = 50 ){ |
| 241 | + global $wgStompQueueName, $wgPendingStompQueueName, $wgLimboStompQueueName; |
| 242 | + |
| 243 | + switch($queue){ |
| 244 | + case 'pending': |
| 245 | + $queue = $wgPendingStompQueueName; |
| 246 | + break; |
| 247 | + case 'limbo': |
| 248 | + $queue = $wgLimboStompQueueName; |
| 249 | + break; |
| 250 | + case 'verified': |
| 251 | + default: |
| 252 | + $queue = $wgStompQueueName; |
| 253 | + break; |
| 254 | + } |
| 255 | + |
| 256 | + $stomp = getDIStompConnection(); |
| 257 | + |
| 258 | + $properties = array( 'ack' => 'client' ); |
| 259 | + if (!is_null($selector)){ |
| 260 | + $properties['selector'] = $selector; |
| 261 | + } |
| 262 | + |
| 263 | + $returned = $stomp->subscribe('/queue/' . $queue, $properties); |
| 264 | + $message = $stomp->readFrame(); |
| 265 | + |
| 266 | + $return = array(); |
| 267 | + |
| 268 | + while ( !empty( $message ) && count( $return ) < $limit ) { |
| 269 | + $return[] = $message; |
| 270 | + $message = $stomp->readFrame(); |
| 271 | + } |
| 272 | + |
| 273 | + return $return; |
| 274 | +} |
| 275 | + |
| 276 | + |
| 277 | +/** |
| 278 | + * Ack all of the messages in the array, thereby removing them from the queue. |
| 279 | + * @param type $messages |
| 280 | + */ |
| 281 | +function stompAckMessages( $messages = array() ){ |
| 282 | + $stomp = getDIStompConnection(); |
| 283 | + foreach ($messages as $message){ |
| 284 | + if (!array_key_exists('redelivered', $message->headers)) { |
| 285 | + $message->headers['redelivered'] = 'true'; |
| 286 | + } |
| 287 | + $result = $stomp->ack($message); |
| 288 | + } |
| 289 | +} |
| 290 | + |
| 291 | +function getDIStompConnection( $renew = false ){ |
| 292 | + global $wgStompServer; |
| 293 | + static $conn = null; |
| 294 | + if ( $conn === null || !$conn->isConnected() || $renew ) { |
| 295 | + if ( $conn !== null && $conn->isConnected() ){ |
| 296 | + $conn->disconnect(); //just to be safe. |
| 297 | + } |
| 298 | + // make a connection |
| 299 | + require_once( "Stomp.php" ); |
| 300 | + $conn = new Stomp( $wgStompServer ); |
| 301 | + // connect |
| 302 | + $conn->connect(); |
| 303 | + } |
| 304 | + return $conn; |
| 305 | +} |
| 306 | + |
| 307 | +function closeDIStompConnection(){ |
| 308 | + $conn = getDIStompConnection(); |
| 309 | + $conn->disconnect(); |
| 310 | +} |
\ No newline at end of file |