public function execute()
 {
     $logger = Logger::getTaggedLogger("corr_id-adyen-{$this->merchantReference}");
     $logger->info("Recording successful capture on account '{$this->account}' with authorization reference " . "'{$this->originalReference}' and order ID '{$this->merchantReference}'.");
     $config = Configuration::getDefaultConfig();
     // Find the details from the payment site in the pending database.
     $logger->debug('Attempting to locate associated message in pending database');
     $db = PendingDatabase::get();
     $dbMessage = $db->fetchMessageByGatewayOrderId('adyen', $this->merchantReference);
     if ($dbMessage && isset($dbMessage['order_id'])) {
         $logger->debug('A valid message was obtained from the pending queue');
         // Add the gateway transaction ID and send it to the completed queue
         $dbMessage['gateway_txn_id'] = $this->originalReference;
         $queueMessage = DonationInterfaceMessage::fromValues($dbMessage);
         SourceFields::addToMessage($queueMessage);
         $config->object('data-store/verified')->push($queueMessage);
         // Remove it from the pending database
         $logger->debug('Removing donor details message from pending database');
         $db->deleteMessage($dbMessage);
     } else {
         // Sometimes we don't have a pending db row because the donor made
         // multiple attempts with the same order ID. It would be nice if
         // Adyen could prevent that, but let's not send a failmail since
         // we'll eventually get the donor details from the payments log
         // when we parse the audit.
         $logger->warning("Could not find donor details for authorization Reference '{$this->originalReference}' " . "and order ID '{$this->merchantReference}'.", $dbMessage);
     }
     return true;
 }
 protected function parseEnvelope(Request $request)
 {
     $requestValues = $request->getValues();
     $secureLog = Logger::getTaggedLogger('RawData');
     $secureLog->info("Incoming message (raw)", $requestValues);
     $messages = array();
     // Can't even check signature without these four
     $required = array('result', 'x_amount', 'x_invoice', 'x_control');
     $missing = array_diff($required, array_keys($requestValues));
     if (count($missing)) {
         $list = implode(',', $missing);
         throw new ListenerDataException("AstroPay message missing required key(s) {$list}.");
     }
     $result = $requestValues['result'];
     if (array_key_exists($result, $this->byResult)) {
         $klass = $this->byResult[$result];
         $message = new $klass();
         $message->constructFromValues($requestValues);
         $secureLog->debug("Found message ", $message);
         $messages[] = $message;
     } else {
         Logger::info("Message ignored: result = {$result}");
     }
     return $messages;
 }
 public function execute(Request $request, Response $response)
 {
     parent::execute($request, $response);
     Logger::info("Starting processing of listener request from {$this->request->getClientIp()}");
     try {
         $this->doIngressSecurity();
         $msgs = $this->parseEnvelope($request);
         if (is_array($msgs)) {
             foreach ($msgs as $msg) {
                 $this->processMessage($msg);
             }
         }
         $this->ackEnvelope();
     } catch (ListenerSecurityException $ex) {
         Logger::notice('Message denied by security policy, death is me.', null, $ex);
         $response->setStatusCode(403, "Not authorized.");
     } catch (ListenerDataException $ex) {
         Logger::error('Listener received request it could not process, death is me.', null, $ex);
         $response->setStatusCode(500, 'Received data could not be processed.');
     } catch (Core\ConfigurationException $ex) {
         Logger::alert('Some sad panda gave me a bad configuration.', null, $ex);
         $response->setStatusCode(500, "Configuration error.");
     } catch (\Exception $ex) {
         Logger::error('Listener threw an unknown exception, death is me.', null, $ex);
         $response->setStatusCode(500, "Unknown listener exception");
     }
     Logger::info('Finished processing listener request');
 }
 /**
  * Instantiates and runs a job defined by a queue message. Depends on
  * the base consumer's damaged message store functionality to either
  * divert messages or stop execution on bad message or job failure.
  * @param array $jobMessage
  * @throws \SmashPig\Core\DataStores\DataSerializationException
  */
 function processMessage($jobMessage)
 {
     if (!isset($jobMessage['php-message-class'])) {
         throw new RuntimeException('Job message missing required key \'php-message-class\'');
     }
     // TODO: encapsulate the reconstitution step elsewhere.
     // FIXME It seems bad that these objects indiscriminately store
     // things as properties. The message is mingled with stuff like
     // php-message-class. Could collide.
     $className = $jobMessage['php-message-class'];
     Logger::info("Hydrating a message with class {$className}");
     $jsonMessage = json_encode($jobMessage);
     Logger::debug("Job payload: {$jsonMessage}");
     $jobObj = KeyedOpaqueStorableObject::fromJsonProxy($className, $jsonMessage);
     if ($jobObj instanceof Runnable) {
         Logger::info('Running job');
         if (!$jobObj->execute()) {
             throw new RuntimeException('Job tells us that it did not successfully execute. ' . 'Sending to damaged message store.');
         }
     } else {
         // We don't know how to run this job type.
         throw new RuntimeException(get_class($jobObj) . ' is not an instance of Runnable. ' . 'Could not execute and sending to damaged message store.');
     }
     $this->successCount += 1;
 }
 /**
  * @param array $post_fields Associative array of fields posted to listener
  * @return bool
  */
 function validate($post_fields = array())
 {
     $url = Configuration::getDefaultConfig()->val('postback-url');
     $ch = curl_init();
     curl_setopt($ch, CURLOPT_URL, $url);
     curl_setopt($ch, CURLOPT_HEADER, 0);
     curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     curl_setopt($ch, CURLOPT_POST, 1);
     // TODO we can put VERIFIED in config and generalize this
     // Always capture the cURL output
     $curlDebugLog = fopen('php://temp', 'r+');
     curl_setopt($ch, CURLOPT_VERBOSE, true);
     curl_setopt($ch, CURLOPT_STDERR, $curlDebugLog);
     $response = $this->curl($ch, $post_fields);
     // Read the logging output
     rewind($curlDebugLog);
     $logged = fread($curlDebugLog, 8192);
     fclose($curlDebugLog);
     Logger::debug("cURL verbose logging: {$logged}");
     if ($response === 'VERIFIED') {
         return true;
     } elseif ($response === 'INVALID') {
         return false;
     } else {
         // TODO: Log txn_id. This is annoying because of the random document formats.
         Logger::debug("Unknown response from PayPal IPN PB: [{$response}].\n" . "Verbose logging: {$logged}");
         // FIXME: The same thing happens for "INVALID" and totally broken
         // responses. Differentiate.
         return false;
     }
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     $this->datastore = BaseQueueConsumer::getQueue($this->getOption('queue'));
     $startTime = time();
     $messageCount = 0;
     // Open the file for read
     $infile = $this->getArgument('file');
     $f = fopen($infile, 'r');
     if (!$f) {
         $this->error("Could not open {$infile} for read", true);
     }
     // Do the loop!
     while (($line = fgets($f)) !== false) {
         if (substr($line, 0, 4) === 'raw=') {
             $message = $this->decodeLegacyMessage($line);
         } else {
             $message = json_decode($line, true);
         }
         if ($message === null) {
             Logger::error("Invalid line: {$line}");
             continue;
         }
         $this->datastore->push($message);
         $messageCount++;
         if ($messageCount % 1000 == 0) {
             print '.';
         }
     }
     print '\\n';
     $elapsedTime = time() - $startTime;
     Logger::info("Imported {$messageCount} messages from {$infile} in {$elapsedTime} seconds.");
 }
 public function execute()
 {
     $this->logger = Logger::getTaggedLogger("corr_id-{$this->correlationId}");
     $this->logger->info("Running capture request job on account '{$this->account}' with reference '{$this->pspReference}' " . "and correlation id '{$this->correlationId}'.");
     // Determine if a message exists in the pending database; if it does not then
     // this payment has already been sent to the verified queue, or there is a
     // problem with the database. If it does exist, we need to check
     // $capture_requested in case we have requested a capture but have not yet
     // received notification of capture success. Either case can occur when a
     // donor submits their credit card details multiple times against a single
     // order ID. We should cancel duplicate authorizations, but leave payments
     // with missing donor details open for potential manual capture.
     $this->logger->debug('Attempting to locate associated message in pending database.');
     $db = PendingDatabase::get();
     $dbMessage = $db->fetchMessageByGatewayOrderId('adyen', $this->merchantReference);
     $success = true;
     $action = $this->determineAction($dbMessage);
     switch ($action) {
         case self::ACTION_PROCESS:
             // Attempt to capture the payment
             /**
              * @var AdyenPaymentsInterface
              */
             $api = $this->getApi();
             $this->logger->info("Attempting capture API call for currency '{$this->currency}', " . "amount '{$this->amount}', reference '{$this->pspReference}'.");
             $captureResult = $api->capture($this->currency, $this->amount, $this->pspReference);
             if ($captureResult) {
                 // Success!
                 $this->logger->info("Successfully captured payment! Returned reference: '{$captureResult}'. " . 'Marking pending database message as captured.');
                 $dbMessage['captured'] = true;
                 $db->storeMessage($dbMessage);
             } else {
                 // Some kind of error in the request. We should keep the pending
                 // db entry, complain loudly, and move this capture job to the
                 // damaged queue.
                 $this->logger->error("Failed to capture payment on account '{$this->account}' with reference " . "'{$this->pspReference}' and order id '{$this->merchantReference}'.", $dbMessage);
                 $success = false;
             }
             break;
         case self::ACTION_REJECT:
             $this->cancelAuthorization();
             // Delete the fraudy donor details
             $db->deleteMessage($dbMessage);
             break;
         case self::ACTION_DUPLICATE:
             // We have already captured one payment for this donation attempt, so
             // cancel the duplicate authorization. If there is a pending db entry,
             // leave it intact for the legitimate RecordCaptureJob.
             $this->cancelAuthorization();
             break;
         case self::ACTION_REVIEW:
             // Don't capture the payment right now, but leave the donor details in
             // the pending database in case the authorization is captured via the console.
             break;
         case self::ACTION_MISSING:
             // Missing donor details - retry later
             throw new RetryableException('Missing donor details');
     }
     return $success;
 }
 public function execute(Request $request, Response $response)
 {
     parent::execute($request, $response);
     Logger::info("Starting processing of listener request from {$this->request->getClientIp()}");
     try {
         $this->doIngressSecurity();
         $soapData = $request->getRawRequest();
         $tl = Logger::getTaggedLogger('RawData');
         $tl->info($soapData);
         $response->sendHeaders();
         /* --- Unfortunately because of how PHP handles SOAP requests we cannot do the fully wrapped
         					loop like we could in the REST listener. Instead it is up to the listener itself to
         					do the required call to $this->processMessage( $msg ).
         
         					It is also expected that inside the handle() context that an exception will throw a SOAP
         					fault through $this->server->fault() instead of doing a $response->kill_response() call.
         			*/
         $this->server->setObject($this);
         $this->server->handle($soapData);
         /* We disable output late in the game in case there was a last minute exception that could
         			be handled by the SOAP listener object inside the handle() context. */
         $response->setOutputDisabled();
     } catch (ListenerSecurityException $ex) {
         Logger::notice('Message denied by security policy, death is me.', null, $ex);
         $response->setStatusCode(403, "Not authorized.");
     } catch (\Exception $ex) {
         Logger::error('Listener threw an unknown exception, death is me.', null, $ex);
         $response->setStatusCode(500, "Unknown listener exception.");
     }
     Logger::info('Finished processing listener request');
 }
 public function execute()
 {
     $logger = Logger::getTaggedLogger("corr_id-{$this->gateway}-{$this->order_id}");
     $logger->info("Deleting message from pending db where gateway = '{$this->gateway}' " . "and order ID='{$this->order_id}'");
     $deleteParams = array('gateway' => $this->gateway, 'order_id' => $this->order_id);
     PendingDatabase::get()->deleteMessage($deleteParams);
     return true;
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     $basePath = 'maintenance/consume-pending/';
     $consumer = new PendingQueueConsumer($this->getOption('queue'), $this->getOptionOrConfig('time-limit', $basePath . 'time-limit'), $this->getOptionOrConfig('max-messages', $basePath . 'message-limit'));
     $startTime = time();
     $messageCount = $consumer->dequeueMessages();
     $elapsedTime = time() - $startTime;
     Logger::info("Processed {$messageCount} pending messages in {$elapsedTime} seconds.");
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     // The logger will print out to the console; and any other log streams that have
     // been configured.
     Logger::info($this->getOption('message'));
     // Other fun functions to know about:
     // - readConsole() - get input from the console
     // - error() - error out and die with a message
 }
 public static function getUtcTimestamp($dateString = 'now', $timeZone = 'UTC')
 {
     try {
         $obj = new DateTime($dateString, new DateTimeZone($timeZone));
         return $obj->getTimestamp();
     } catch (Exception $ex) {
         Logger::warning('Could not get timestamp from string', $dateString, $ex);
         return null;
     }
 }
 public function processMessage($message)
 {
     $logIdentifier = "message with gateway {$message['gateway']}" . " and order ID {$message['order_id']}";
     if ($this->paymentsInitialDatabase->isTransactionFailed($message)) {
         // Throw the message out if it's already failed
         Logger::info("Skipping failed {$logIdentifier}");
     } else {
         Logger::info("Storing {$logIdentifier} in database");
         $this->pendingDatabase->storeMessage($message);
     }
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     // Get some defaults from configuration
     $basePath = 'maintenance/job-runner/';
     $consumer = new JobQueueConsumer($this->getOption('queue'), $this->getOptionOrConfig('time-limit', $basePath . 'time-limit'), $this->getOptionOrConfig('max-messages', $basePath . 'message-limit'));
     $startTime = time();
     $messageCount = $consumer->dequeueMessages();
     $successCount = $consumer->getSuccessCount();
     $elapsedTime = time() - $startTime;
     Logger::info("Processed {$messageCount} ({$successCount} successful) jobs in {$elapsedTime} seconds.");
 }
 /**
  * Renames the current logging context. Effects the log prefix used for all
  * events under this context. May have adverse effects on logstreams that log
  * in real time (IE: Syslog) because they will have logged items under the old
  * context name.
  *
  * @param string   $newName     New name for the current context
  * @param bool     $addLogEntry If false will not create a log line stating the name change
  *
  * @return string The old name of this context
  */
 public function renameContext($newName, $addLogEntry = true)
 {
     $old = $this->contextNames[0];
     if ($addLogEntry) {
         Logger::info("Renaming logging context '{$old}' to '{$newName}'.");
     }
     $this->contextNames[0] = $newName;
     foreach ($this->logStreams as $stream) {
         $stream->renameContext($this->contextNames, $old);
     }
     return $old;
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     Logger::info('Info log message');
     Logger::debug('Debug log message');
     Logger::notice('Notice...');
     Logger::getTaggedLogger('RawData')->info('This should be tagged RawData');
     Logger::warning('Warning!', array('foo' => 'bar'));
     try {
         $this->throwException();
     } catch (SmashPigException $ex) {
         Logger::error('ERROR!!!!', null, $ex);
     }
 }
 public function parse($path)
 {
     $csv = new HeadedCsvReader($path, ',', 4096, 0);
     while ($csv->valid()) {
         try {
             $this->parseLine($csv);
             $csv->next();
         } catch (DataFileException $ex) {
             Logger::error($ex->getMessage());
         }
     }
     return $this->fileData;
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     $this->datastore = new StompDataStore($this->getOption('queue'));
     $maxMessages = $this->getOption('max-messages');
     $startTime = time();
     $messageCount = 0;
     $raw = $this->getOption('raw');
     $ignore = $this->getOption('no-ack');
     // Construct the selectors
     $argId = 0;
     $selectors = array();
     while ($this->hasArgument($argId)) {
         $selectors[] = $this->getArgument($argId);
         $argId += 1;
     }
     // Open the file for write-append
     $outfile = $this->getOption('outfile');
     $f = fopen($outfile, 'a');
     if (!$f) {
         $this->error("Could not open {$outfile} for write append", true);
     }
     // Do the loop!
     do {
         // This is actually quite inefficient; but this whole thing is a hack so meh!
         // ...Ideally we would take the JSON from the store directly instead of walking
         // it through an object
         try {
             $queueObj = $this->datastore->queueGetObject(null, null, $selectors, !$raw);
             if (!$queueObj) {
                 break;
             }
             if ($raw) {
                 fwrite($f, 'raw' . "=" . json_encode($queueObj) . "\n");
             } else {
                 fwrite($f, get_class($queueObj) . "=" . $queueObj->toJson(false) . "\n");
             }
         } catch (DataSerializationException $ex) {
             // We probably caught an anti-message here; log the exception and continue on
             Logger::warning("Possibly caught an antimessage. Not adding to file.", null, $ex);
         }
         if ($ignore) {
             $this->datastore->queueIgnoreObject();
         } else {
             $this->datastore->queueAckObject();
         }
         $messageCount += 1;
     } while ($maxMessages === 0 || $messageCount < $maxMessages);
     $elapsedTime = time() - $startTime;
     Logger::info("Dumped {$messageCount} messages to {$outfile} in {$elapsedTime} seconds.");
 }
 public function execute(ListenerMessage $msg)
 {
     $destinationQueue = $msg->getDestinationQueue();
     if ($destinationQueue) {
         $queue = Context::get()->getConfiguration()->object("data-store/{$destinationQueue}");
         $queueMsg = $msg->normalizeForQueue();
         SourceFields::addToMessage($queueMsg);
         $queue->push($queueMsg);
     } else {
         $class = get_class($msg);
         Logger::warning("Ignoring message of type {$class}", $msg);
     }
     return true;
 }
 /**
  * Will run all the actions that are loaded (from the 'actions' configuration
  * node) and that are applicable to this message type. Will return true
  * if all actions returned true. Otherwise will return false. This implicitly
  * means that the message will be re-queued if any action fails. Therefore
  * all actions need to be idempotent.
  *
  * @returns bool True if all actions were successful. False otherwise.
  */
 public function runActionChain()
 {
     Logger::info("Received new report from Adyen: {$this->pspReference}. Generated: {$this->eventDate}.", $this->reason);
     $jobQueue = BaseQueueConsumer::getQueue('jobs-adyen');
     if (strpos($this->pspReference, 'settlement_detail_report') === 0) {
         $jobObject = DownloadReportJob::factory($this->merchantAccountCode, $this->reason);
         // FIXME: write queue wrapper to do these next two steps
         SourceFields::addToMessage($jobObject);
         $jobArray = json_decode($jobObject->toJson(), true);
         $jobQueue->push($jobArray);
     } else {
         // We don't know how to handle this report yet
         Logger::notice("Do not know how to handle report with name '{$this->pspReference}'");
     }
     return parent::runActionChain();
 }
 protected function downloadReport($reportInfo)
 {
     $id = $reportInfo['ReportId'];
     // Remove common prefix from report type
     $type = str_replace('_GET_FLAT_FILE_OFFAMAZONPAYMENTS_', '', $reportInfo['ReportType']);
     if (array_search($id, $this->downloadedIds) === false) {
         Logger::debug("Downloading report dated {$reportInfo['AvailableDate']} with id: {$id}");
         $report = $this->reportsClient->getReport(array('report_id' => $id));
         $date = substr($reportInfo['AvailableDate'], 0, 10);
         $path = "{$this->downloadPath}/{$date}-{$type}{$id}.csv";
         Logger::info("Saving report to {$path}");
         file_put_contents($path, $report['ResponseBody']);
     } else {
         Logger::debug("Skipping downloaded report with id: {$id}");
     }
 }
 public function execute(ListenerMessage $msg)
 {
     // Bail out if not a refund
     if (get_class($msg) !== self::MESSAGE_CLASS) {
         return true;
     }
     $refundId = $msg->getGatewayTransactionId();
     Logger::info("Looking up ID of original transaction for refund {$refundId}");
     try {
         $parentId = AmazonApi::findRefundParentId($refundId);
         $msg->setParentId($parentId);
         return true;
     } catch (SmashPigException $ex) {
         Logger::error($ex->getMessage());
         return false;
     }
 }
 protected function parseEnvelope(Request $request)
 {
     // Symfony's framework gives us each header's value as an array
     // (to account for potential repeated headers?
     // IpnHandler's constructor expects scalar values, so we flatten them
     $headers = array();
     foreach ($request->headers->all() as $header => $annoyingArray) {
         if (count($annoyingArray) !== 1) {
             throw new ListenerDataException("header '{$header}' should have a single value");
         }
         $headers[$header] = $annoyingArray[0];
     }
     $json = $request->getRawRequest();
     $secureLog = Logger::getTaggedLogger('RawData');
     $secureLog->info('Incoming message (raw)', array('headers' => $headers, 'body' => $json));
     $messages = array();
     try {
         $amazonHandlerMessage = AmazonApi::createIpnHandler($headers, $json);
     } catch (\Exception $ex) {
         // FIXYOU: IpnHandler should use exception subclasses or error codes
         // Assuming here that IpnHandler's problem was with the signature
         // We can get away with throwing ListenerSecurityException here
         // because of how RestListener is implemented and because we only
         // process one message per request
         // Bad form, but it would be odd to hold this till doMessageSecurity
         throw new ListenerSecurityException($ex->getMessage());
     }
     $messageValues = $amazonHandlerMessage->toArray();
     $type = $messageValues['NotificationType'];
     if (array_key_exists($type, $this->messageClasses)) {
         $byStatus = $this->messageClasses[$type];
         $status = $this->getMessageStatus($messageValues, $type);
         if (array_key_exists($status, $byStatus)) {
             $klass = $byStatus[$status];
             $message = new $klass($messageValues);
             $secureLog->debug('Created message', $message);
             $messages[] = $message;
         } else {
             Logger::info("Message ignored: status = {$status}");
         }
     } else {
         Logger::info("Message ignored: notificationType = {$type}");
     }
     return $messages;
 }
 public function execute(ListenerMessage $msg)
 {
     // only close after successful capture
     if (get_class($msg) !== self::MESSAGE_CLASS) {
         return true;
     }
     $config = Context::get()->getConfiguration();
     $client = $config->object('payments-client', true);
     $captureId = $msg->getGatewayTransactionId();
     $orderReferenceId = substr($captureId, 0, 19);
     Logger::info("Closing order reference {$orderReferenceId}");
     $response = $client->closeOrderReference(array('amazon_order_reference_id' => $orderReferenceId))->toArray();
     if (!empty($response['Error'])) {
         Logger::info("Error losing order reference {$orderReferenceId}: " . $response['Error']['Code'] . ': ' . $response['Error']['Message']);
         return false;
     }
     return true;
 }
 public function parseFile($path)
 {
     $this->path = $path;
     $this->file = fopen($path, 'r');
     $ignoreLines = 1;
     for ($i = 0; $i < $ignoreLines; $i++) {
         fgets($this->file);
     }
     while ($line = fgetcsv($this->file, 0, ';', '"', '\\')) {
         try {
             $this->parseLine($line);
         } catch (NormalizationException $ex) {
             // TODO: actually throw these below
             Logger::error($ex->getMessage());
         }
     }
     fclose($this->file);
     return $this->fileData;
 }
 /**
  * Creates an appropriate derived AdyenMessage instance from the object received
  * during the SOAP transaction.
  *
  * The magic here is looking at the eventCode field, normalizing it, and then
  * loading the class if it exists.
  *
  * @param \SmashPig\PaymentProviders\Adyen\WSDL\NotificationRequestItem $obj
  */
 public static function getInstanceFromWSDL(NotificationRequestItem $msgObj)
 {
     // Adyen events come in as UPPER_CASE_UNDERSCORE_DELIMITED, we turn this
     // into UpperCaseUnderscoreDelimited
     $className = implode('', array_map('ucwords', explode('_', strtolower($msgObj->eventCode))));
     $className = 'SmashPig\\PaymentProviders\\Adyen\\ExpatriatedMessages\\' . $className;
     if (class_exists($className)) {
         Logger::debug("Attempting construction of '{$className}'");
         $obj = new $className();
     } else {
         Logger::debug("Class not found '{$className}'");
         return false;
     }
     if ($obj instanceof AdyenMessage) {
         $obj->constructFromWSDL($msgObj);
     } else {
         throw new ListenerDataException("Instantiated object '{$className}' does not inherit from AdyenMessage'!");
     }
     return $obj;
 }
 /**
  * Do the actual work of the script.
  */
 public function execute()
 {
     $this->datastore = Context::get()->getConfiguration()->object('data-store/' . $this->getArgument(0, 'test'), false);
     // Generate a whole bunch of random data
     while (count($this->testObjects) < 10) {
         $this->testObjects[] = TestObject::factory();
     }
     // And repeat the objects and inject so we have something else to find
     foreach ($this->testObjects as $obj) {
         $this->datastore->addObject($obj);
         $this->datastore->addObject(TestObject::factory($obj->correlationId));
     }
     // Mix up the order of the objects to simulate real life
     shuffle($this->testObjects);
     // Now attempt to find them and their pairs!
     $this->datastore = Context::get()->getConfiguration()->object('data-store/' . $this->getArgument(0, 'test'), false);
     foreach ($this->testObjects as $obj) {
         $obj1 = $this->datastore->queueGetObject(null, $obj->correlationId);
         if ($obj1 !== null) {
             $this->datastore->queueAckObject();
         } else {
             $this->error("Could not find original object with id {$obj->correlationId}");
             continue;
         }
         $obj2 = $this->datastore->queueGetObject(null, $obj->correlationId);
         if ($obj2 !== null) {
             $this->datastore->queueAckObject();
         } else {
             $this->error("Could not find secondary object with id {$obj->correlationId}");
             continue;
         }
         $obj3 = $this->datastore->queueGetObject(null, $obj->correlationId);
         if ($obj3 !== null) {
             $this->datastore->queueAckObject();
             $this->error("Found tertiary object with id {$obj3->correlationId} " . "while looking for id {$obj->correlationId}");
             continue;
         }
         Logger::info("Successfully found id {$obj->correlationId}");
     }
     Logger::info("Done");
 }
 public function cancel($pspReference)
 {
     $data = new WSDL\cancel();
     $data->modificationRequest = new WSDL\ModificationRequest();
     $data->modificationRequest->merchantAccount = $this->account;
     $data->modificationRequest->originalReference = $pspReference;
     $tl = new TaggedLogger('RawData');
     $tl->info('Launching SOAP cancel request', $data);
     try {
         $resp = $this->soapClient->cancel($data);
     } catch (\Exception $ex) {
         Logger::error('SOAP cancel request threw exception!', null, $ex);
         return false;
     }
     if ($resp->cancelResult->response == '[cancel-received]') {
         return $resp->cancelResult->pspReference;
     } else {
         Logger::error('SOAP cancel request did not work as expected!', $resp);
         return false;
     }
 }
 public function execute(Request $request, Response $response)
 {
     $this->config = Configuration::getDefaultConfig();
     $requestValues = $request->getValues();
     // Don't store blank messages.
     if (empty($requestValues)) {
         return;
     }
     // Don't store invalid messages.
     $valid = $this->config->object('api')->validate($requestValues);
     if (!$valid) {
         // This will tell them to resend later.
         $response->setStatusCode(403, 'Failed verification');
         return false;
     }
     // Dump the request right into the queue with no validation.
     $job = new Job();
     $job->payload = $requestValues;
     $this->config->object('data-store/jobs-paypal')->push($job);
     Logger::info('Pushed new message to jobs-paypal: ' . print_r($requestValues, true));
 }
 /**
  * Will run all the actions that are loaded (from the 'actions' configuration
  * node) and that are applicable to this message type. Will return true
  * if all actions returned true. Otherwise will return false. This implicitly
  * means that the message will be re-queued if any action fails. Therefore
  * all actions need to be idempotent.
  *
  * @returns bool True if all actions were successful. False otherwise.
  */
 public function runActionChain()
 {
     $retval = true;
     // TODO: Cache this?
     $actions = Context::get()->getConfiguration()->val('actions');
     foreach ($actions as $actionClassName) {
         $action = new $actionClassName();
         if ($action instanceof IListenerMessageAction) {
             Logger::debug("Running action {$actionClassName}.");
             if (!$action->execute($this)) {
                 Logger::info("Action {$actionClassName} did not execute properly, will re-queue.");
                 $retval = false;
                 break;
             } else {
                 Logger::debug("Action returned success.");
             }
         } else {
             Logger::error("Entry under actions node '{$actionClassName}' does not implement IListenerActionMessage");
         }
     }
     return $retval;
 }