diff --git a/.gitignore b/.gitignore
index 7f62d4632f1b0787b09b3544610243f011ed7599..f12ca75fffa864ba5f9d3d8f07c0e4288d88b907 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,6 @@
 /src/main/resources/*.pem
 
 /src/test/resources/pemtest.pem
-/src/test/resources/integration-test-data.ini
\ No newline at end of file
+/src/test/resources/integration-test-data.ini
+
+.DS_Store
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/RpcNetidClient.php b/src/main/edu/wisc/doit/RpcNetidClient.php
index 9f626a56e67d12aa6a7eb22cb5bbdccc7508253d..7a6a9004cdcaeb1ca61cc58e9a89f463fc41dc69 100644
--- a/src/main/edu/wisc/doit/RpcNetidClient.php
+++ b/src/main/edu/wisc/doit/RpcNetidClient.php
@@ -31,20 +31,14 @@ interface RpcNetidClient {
 	function lockStatus ( $uid );
 
 	/**
-	 * Checks the credentials (NetID, WisCard number, and DOB) of a UW-Madison person
-	 * @param string $uid  the uid of the user to search for (typically the NetID)
-	 * @param DateTime $birthdate  DOB for the UW-Person as a DateTime
-	 * @param int $wiscard  a 10-digit WisCard number
-	 * @return bool  true if credentials are verified, false if credential is unverified
-	 * @throws edu\wisc\doit\RpcNetidClientSoapException if the response is unexcepted from web service
-	 */
-	function credentialCheck ( $uid, \DateTime $birthdate, $wiscard = null  );
-
-	/**
-	 * Checks that the password meets require criteria and then changes password if it meets criteria
+	 * Changes the password for a given NetID
+	 * 
+	 * <p><b>WARNING READ THIS IMPORTANT DO NOT SKIP:</b> This method DOES NOT check if the password
+	 * meets complexity requirements. You MUST check the complexity using {@link RpcNetidClient::passwordChoicePolicyCheck}</p>
+	 * 
 	 * @param string $uid  the uid of the user to search for (typically the NetID)
 	 * @param string $password  the password to be checked for policy adherence and replace current password
-	 * @return bool  True if password was changed successfully, false if password did not meet password criteria
+	 * @return bool  true if password was changed successfully, false otherwise
 	 * @throws edu\wisc\doit\RpcNetidClientSoapException if unexpected response code from SOAP service
 	 */
 	function changePassword ( $uid, $password );
@@ -53,11 +47,11 @@ interface RpcNetidClient {
 	 * Validates the answers to the NetID security questions for a given user
 	 * @param string $uid  the uid of the user to search for (typically the NetID)
 	 * @param RpcNetidStructQuestion[] $questions  array containing security questions for the user (Number and Answer required)
-	 * @param string $ip  ip address of the user (optional)
+	 * @param string $ip  ip address of the user
 	 * @return bool  true if the answers are correct, false otherwise
 	 * @throws edu\wisc\doit\RpcNetidClientSoapException if unexpected response from web service 
 	 */
-	function checkAnswers ( $uid, array $questions, $ip = "" );
+	function checkAnswers ( $uid, array $questions, $ip );
 	
 	/**
 	 * Retrieves the recovery email attached to the given user's uid
@@ -90,6 +84,29 @@ interface RpcNetidClient {
 	 * @return RpcNetidStructValidationResponse  indicates if valid and the reason for invalid
 	 */
 	function passwordChoicePolicyCheck( $password );
+	
+	/**
+	 * Verifies the identity of a UW-Madison user using his or her NetID, date of birth, 
+	 * and optional Wiscard number.
+	 * 
+	 * <p>This method will take into account the user's LOA (level of assurance). If the user
+	 * is LOA2 or higher, the WisCard number is required. Otherwise, it is ignored.</p>
+	 * 
+	 * <p>The method returns a {@link RpcNetidStructValidationResponse} object. If 
+	 * {@link RpcNetidStructValidationResponse::getIsValid} returns true, the identity is verified.
+	 * Otherwise, it will return false with zero or more reasons returned by
+	 * {@link RpcNetidStructValidationResponse::getReasons}</p>
+	 * 
+	 * <p>If the user requires a Wiscard and one was not provided, the method will return a {@link RpcNetidStructValidationResponse}
+	 * object with {@link RpcNetidStructValidationResponse::REASON_NEEDS_WISCARD} as one of the reasons in the array.</p>
+	 * 
+	 * @param string $uid  user's NetID
+	 * @param \DateTime $birthdate  user's date of birth
+	 * @param int $wiscard  user's 11-digit Wiscard number
+	 * @return RpcNetidStructValidationResponse  validation response including reasons for failure.
+	 */
+	function checkLOA( $uid, \DateTime $birthdate, $wiscard = null );
+	
 }
 
 /**
diff --git a/src/main/edu/wisc/doit/RpcNetidClientSoap.php b/src/main/edu/wisc/doit/RpcNetidClientSoap.php
index 0433fb0b9e4e8a7e0d6ad56ad7521bb9384bceb0..81a8c1a0f37849ab95ac38098fca554727f327f2 100644
--- a/src/main/edu/wisc/doit/RpcNetidClientSoap.php
+++ b/src/main/edu/wisc/doit/RpcNetidClientSoap.php
@@ -93,35 +93,66 @@ class RpcNetidClientSoap implements RpcNetidClient {
 		RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
 	}
 	
-	
 	/**
 	 * (non-PHPdoc)
-	 * @see \edu\wisc\doit\RpcNetidClient::credentialCheck()
+	 * @see \edu\wisc\doit\RpcNetidClient::checkLOA()
 	 */
-	function credentialCheck ( $uid, \DateTime $birthdate, $wiscard = null ){
-		
-		if ( is_string($uid) !== true ){
-			throw new \DomainException("uid parameter must be a string");
-		} 
+	public function checkLOA( $uid, \DateTime $birthdate, $wiscard = null ) {
 		
-		if( is_int($wiscard) !== true && ($wiscard !== null)){
-			throw new \DomainException("wiscard parameter must be an int or empty");
+		if ( is_string( $uid ) !== true or empty( $uid ) === true  ) {
+			throw new \InvalidArgumentException( "uid must be a nonempty string" );
 		}
 		
-		if($wiscard === null) { $wiscard = ""; }
-		
-		$result = $this->getSoapClient()->credentialCheck( array( 'uid' => $uid, 'cardid' => $wiscard, 'birthdate' => $birthdate->format("m-d-Y") ) );
-		
-		if($result->result === 200){
-			return true;
+		if ( is_null( $wiscard ) === false  ) {
+			if ( is_int( $wiscard ) !== true ) {
+				throw new \InvalidArgumentException( "wiscard must be at least a 10-digit integer");
+			}
+			if ( strlen(strval($wiscard)) < 10 ) {
+				throw new \DomainException( "wiscard must be at least a 10-digit integer");
+			}
 		}
 		
-		if($result->result === 400){
-			return false;
+		$parameters = array();
+		$parameters['uid'] = strval( $uid );
+		$parameters['birthdate'] = $birthdate->format("m-d-Y");
+		if ( is_null( $wiscard ) === false ) { $parameters['cardid'] = strval( $wiscard ); }
+		
+		$result = $this->getSoapClient()->checkLOA( $parameters );
+		
+		if ( isset( $result->result ) !== true ) {
+			throw new RpcNetidClientSoapException( "Web service did not return a result code", RpcNetidClientSoapException::UNEXPECTED_RESPONSE );
+		}
+		
+		switch( $result->result ) {
+			case 200:
+				return new RpcNetidStructValidationResponse(true, array());
+				break;
+			case 400:
+				return new RpcNetidStructValidationResponse(false, array());
+				break;
+			case 401:
+				throw new RpcNetidClientSoapException("Web service returned 401: invalid input parameters", 
+					RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
+				break;
+			case 402:
+				throw new RpcNetidClientSoapException("Web service returned 402: no PVI found for uid",
+					RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
+				break;
+			case 403:
+				throw new RpcNetidClientSoapException("Web service returned 403: no LOA found for uid",
+					RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
+				break;
+			case 404:
+				throw new RpcNetidClientSoapException("Web service returned 404: no Wiscard eligibility data found for uid",
+					RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
+				break;
+			case 405:
+				return new RpcNetidStructValidationResponse(false, array(RpcNetidStructValidationResponse::REASON_NEEDS_WISCARD ) );
+				break;
 		}
 		
 		throw new RpcNetidClientSoapException("Unexpected status code: {$result->result}",
-		RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
+			RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
 		
 	}
 			
@@ -145,12 +176,12 @@ class RpcNetidClientSoap implements RpcNetidClient {
 			return true;
 		}
 		
-		if( $result->result === 401 ){
+		if( $result->result === 400 or $result->result === 401 ){
 			return false;
 		}
 			
 		throw new RpcNetidClientSoapException("Unexpected status code: {$result->result}",
-		RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
+			RpcNetidClientSoapException::UNEXPECTED_STATUS_CODE );
 
 	}
 			
@@ -158,24 +189,22 @@ class RpcNetidClientSoap implements RpcNetidClient {
 	 * (non-PHPdoc)
 	 * @see \edu\wisc\doit\RpcNetidClient::checkAnswers()
 	 */
-	public function checkAnswers( $uid, array $questions, $ip = "" ){
+	public function checkAnswers( $uid, array $questions, $ip ){
 		
 		if( is_string($uid) !== true) {
 			throw new \InvalidArgumentException("uid parameter must be a string");
 		}
-		
-		if ( empty( $ip ) === false ) {
-			
-			if ( is_string( $ip ) !== true) {
-				throw new \InvalidArgumentException("ip parameter must be a string");
-			}
-			
-			if ( filter_var( $ip, FILTER_VALIDATE_IP ) === false ) {
-				throw new \DomainException( "Invalid IP address: $ip" );
-			}
+
 			
+		if ( is_string( $ip ) !== true) {
+			throw new \InvalidArgumentException("ip parameter must be a string");
 		}
 		
+		if ( filter_var( $ip, FILTER_VALIDATE_IP ) === false ) {
+			throw new \DomainException( "Invalid IP address: $ip" );
+		}
+
+		
 		// Create array of parameters for the SOAP client
 		$questionsParam = array( 'QuestionPair' => array() );
 		
@@ -360,7 +389,7 @@ class RpcNetidClientSoap implements RpcNetidClient {
  * Exception class for {@link RpcNetidClientSoap}
  */
 class RpcNetidClientSoapException extends RpcNetidClientException {
-	const UNEXPECTED_STATUS_CODE = 100;
-	const UNEXPECTED_RESPONSE = 101;
+	const UNEXPECTED_STATUS_CODE = 100;   // web service returns status code that cannot be handled (runtime exception)
+	const UNEXPECTED_RESPONSE = 101;  // web service returns content that is not expected
 	const INVALID_EMAIL = 102;
 }
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/RpcNetidStructValidationResponse.php b/src/main/edu/wisc/doit/RpcNetidStructValidationResponse.php
index 9c7e2c13a6ab31724028815437671c3057f13d81..729d61cd4b996a0e2cbbdab13d9b0eac75a29f5d 100644
--- a/src/main/edu/wisc/doit/RpcNetidStructValidationResponse.php
+++ b/src/main/edu/wisc/doit/RpcNetidStructValidationResponse.php
@@ -3,7 +3,8 @@
 namespace edu\wisc\doit;
 
 /**
- * Represents a validation response for a NetID password
+ * Represents a validation response. This can be used when information about why something
+ * is invalid needs to be communicated.
  */
 
 class RpcNetidStructValidationResponse {
@@ -11,9 +12,14 @@ class RpcNetidStructValidationResponse {
 	/** @var bool  true if valid, false if invalid */
 	private $isValid;
 	
-	/** @var string[]  the reasons for a password being invalid */
+	/** @var mixed[]  the reasons for being invalid */
 	private $reasons;
 	
+	/**
+	 * @var int  encoded reason for when a user needs a Wiscard number
+	 */
+	const REASON_NEEDS_WISCARD = 100;
+	
 	/**
 	 * @param bool $isValid
 	 * @param string $reason
diff --git a/src/test/RpcNetidClientSoapTest.php b/src/test/RpcNetidClientSoapTest.php
index a19db3bf2ff15f61cb43009d1e3a569f0d9afecb..04bb59d0011d2d9257223dada9d8e524be802e01 100644
--- a/src/test/RpcNetidClientSoapTest.php
+++ b/src/test/RpcNetidClientSoapTest.php
@@ -5,6 +5,7 @@ use edu\wisc\doit\RpcNetidClientSoapConfig;
 use edu\wisc\doit\RpcNetidStructGetQuestionsRequest;
 use edu\wisc\doit\RpcNetidStructQuestion;
 use edu\wisc\doit\RpcNetidStructNetidResponse;
+use edu\wisc\doit\RpcNetidStructValidationResponse;
 
 /**
  * Unit tests for the {@link edu\wisc\doit\RpcNetidClientSoap} class.
@@ -148,80 +149,6 @@ class RpcNetidClientSoapTest extends PHPUnit_Framework_TestCase {
 		$this->assertEquals("Locked out of password reset - too many attempts", $returnValue->getReason());
 	}
 	
-	/**
-	 * @test
-	 * @expectedException DomainException
-	 */
-	function credentialCheck_UidIsNotString(){
-		$client = new RpcNetidClientSoap($this->mockSoapClient);
-		$birthdate = new \DateTime();
-		$returnValue = $client->credentialCheck(123, $birthdate, 123);
-	}
-	
-	/**
-	 * @test
-	 * @expectedException DomainException
-	 */
-	function credentialCheck_WiscardIsNotInt(){
-		$client = new RpcNetidClientSoap($this->mockSoapClient);
-		$birthdate = new \DateTime();
-		$returnValue = $client->credentialCheck("uid", $birthdate, "A123");
-	}
-
-	
-	/**
-	 * @test
-	 * @expectedException edu\wisc\doit\RpcNetidClientSoapException
-	 * @expectedExceptionCode 100
-	 */
-	function credentialCheck_UnexpectedStatusCodeFromWebService(){
-		$result = new stdClass();
-		$result->result = 500;
-		$this->mockSoapClient->expects($this->any())->method('credentialCheck')->will($this->returnValue($result));
-		$client = new RpcNetidClientSoap($this->mockSoapClient);
-		$birthdate = new \DateTime();
-		$returnValue = $client->credentialCheck("uid", $birthdate, 123);
-	}
-	
-	/**
-	 * @test Valid call to checkCredential that returns false
-	 */
-	function credentialCheck_validCall_ReturnsFalse(){
-		$result = new stdClass();
-		$result->result = 400;
-		$this->mockSoapClient->expects($this->any())->method('credentialCheck')->will($this->returnValue($result));
-		$client = new RpcNetidClientSoap($this->mockSoapClient);
-		$birthdate = new \DateTime();
-		$returnValue = $client->credentialCheck("uid", $birthdate, 123);
-		$this->assertFalse($returnValue);
-	}
-	
-	/**
-	 * @test Valid call to checkCredential that returns true
-	 */
-	function credentialCheck_validCall_ReturnsTrue(){
-		$result = new stdClass();
-		$result->result = 200;
-		$this->mockSoapClient->expects($this->any())->method('credentialCheck')->will($this->returnValue($result));
-		$client = new RpcNetidClientSoap($this->mockSoapClient);
-		$birthdate = new \DateTime();
-		$returnValue = $client->credentialCheck("uid", $birthdate, 123);
-		$this->assertTrue($returnValue);
-	}
-	
-	/**
-	 * @test Valid call to checkCredential that returns true with empty wiscard
-	 */
-	function credentialCheck_validCallWithEmpty_ReturnsTrue(){
-		$result = new stdClass();
-		$result->result = 200;
-		$this->mockSoapClient->expects($this->any())->method('credentialCheck')->will($this->returnValue($result));
-		$client = new RpcNetidClientSoap($this->mockSoapClient);
-		$birthdate = new \DateTime();
-		$returnValue = $client->credentialCheck("uid", $birthdate);
-		$this->assertTrue($returnValue);
-	}
-	
 	/**
 	 * @test
 	 * @expectedException DomainException
@@ -602,5 +529,152 @@ class RpcNetidClientSoapTest extends PHPUnit_Framework_TestCase {
 		$this->assertTrue($returnValue->getIsValid());
 		
 	}
+	
+	/* checkLOA tests ------------------------------- */
+	
+	/**
+	 * @test Wiscard is less than 10 digits throws exception
+	 * @expectedException DomainException
+	 */
+	function checkLOA_wiscard_less_than_10_digits_throws() {
+		
+		$result = new stdClass();
+		$result->result = 200;
+		
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$client->checkLOA( "jsmith", new \DateTime(), 12345 );
+		
+	}
+	
+	/**
+	 * @test returns false if 400 is returned by web service
+	 */
+	function checkLOA_400_returns_false() {
+		
+		$result = new stdClass();
+		$result->result = 400;
+		
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$returned = $client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+		$this->assertInstanceOf('edu\wisc\doit\RpcNetidStructValidationResponse', $returned );
+		$this->assertFalse( $returned->getIsValid() );
+		
+	}
+	
+	/**
+	 * @test throws exception if web service returns 401 (invalid parameters)
+	 * @expectedException edu\wisc\doit\RpcNetidClientSoapException
+	 * @expectedExceptionCode 100
+	 */
+	function checkLOA_401_throws() {
+	
+		$result = new stdClass();
+		$result->result = 401;
+	
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+	
+	}
+	
+	/**
+	 * @test throws exception if web service returns 402 (No PVI found for UID)
+	 * @expectedException edu\wisc\doit\RpcNetidClientSoapException
+	 * @expectedExceptionCode 100
+	 */
+	function checkLOA_402_throws() {
+	
+		$result = new stdClass();
+		$result->result = 402;
+	
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+	
+	}
+	
+	/**
+	 * @test throws exception if web service returns 403 (No LOA found for UID)
+	 * @expectedException edu\wisc\doit\RpcNetidClientSoapException
+	 * @expectedExceptionCode 100
+	 */
+	function checkLOA_403_throws() {
+	
+		$result = new stdClass();
+		$result->result = 403;
+	
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+	
+	}
+	
+	/**
+	 * @test throws exception if web service returns 404 (No Wiscard eligibility found for user)
+	 * @expectedException edu\wisc\doit\RpcNetidClientSoapException
+	 * @expectedExceptionCode 100
+	 */
+	function checkLOA_404_throws() {
+	
+		$result = new stdClass();
+		$result->result = 404;
+	
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+	
+	}
+	
+	/**
+	 * @test returns false validation object with reason if web service returns 405 (Person is LOA2, but the Wiscard was missing)
+	 */
+	function checkLOA_405_returns_false_with_reason() {
+		
+		$result = new stdClass();
+		$result->result = 405;
+		
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$returned = $client->checkLOA( "jsmith", new \DateTime() );
+		
+		$this->assertInstanceOf('edu\wisc\doit\RpcNetidStructValidationResponse', $returned );
+		$this->assertFalse( $returned->getIsValid() );
+		$this->assertContains( RpcNetidStructValidationResponse::REASON_NEEDS_WISCARD, $returned->getReasons() );
+		
+	}
+	
+	/**
+	 * @test returns true validation object when web service returns 200
+	 */
+	function checkLOA_200_returns_true() {
+		
+		$result = new stdClass();
+		$result->result = 200;
+		
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$returned = $client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+		
+		$this->assertInstanceOf('edu\wisc\doit\RpcNetidStructValidationResponse', $returned );
+		$this->assertTrue( $returned->getIsValid() );
+		
+	}
+	
+	/**
+	 * @test throws exception if an unexpected response code was received by web service 
+	 * @expectedException edu\wisc\doit\RpcNetidClientSoapException
+	 * @expectedExceptionCode 100
+	 */
+	function checkLOA_500_throws_exception() {
+		$result = new stdClass();
+		$result->result = 500;
+		
+		$this->mockSoapClient->expects($this->any())->method('checkLOA')->will($this->returnValue($result));
+		$client = new RpcNetidClientSoap($this->mockSoapClient);
+		$client->checkLOA( "jsmith", new \DateTime(), 12345678901 );
+		
+	}
 
 }
diff --git a/src/test/integration-tests/RpcNetidClientSoapIT.php b/src/test/integration-tests/RpcNetidClientSoapIT.php
index 991ae77fcadd772c93f1c7449b551dbd48572ca7..b85a8856aad3c80f1b3c6215347836dcdafb9030 100644
--- a/src/test/integration-tests/RpcNetidClientSoapIT.php
+++ b/src/test/integration-tests/RpcNetidClientSoapIT.php
@@ -3,6 +3,7 @@
 use edu\wisc\doit\RpcNetidClientSoapConfig;
 use edu\wisc\doit\RpcNetidClientSoap;
 use edu\wisc\doit\RpcNetidStructQuestion;
+use edu\wisc\doit\RpcNetidStructValidationResponse;
 
 /**
  * Integration tests for edu\wisc\doit\RpcNetidClientSoap
@@ -77,30 +78,49 @@ class RpcNetidClientSoapIT extends PHPUnit_Framework_TestCase {
 	}
 	
 	/** @test */
-	public function credentialCheck_validIT() {
-		$result = self::$client->credentialCheck( 
+	public function checkLOA_Loa2valid() {
+		$result = self::$client->checkLOA( 
 				self::$testData['user']['uid'],
 				new DateTime( self::$testData['user']['birthdate'] ),
 				intval( self::$testData['user']['wiscard'] ) );
-		$this->assertTrue( $result );
+		$this->assertTrue( $result->getIsValid() );
 	}
 	
 	/** @test test valid credentials for an LOA1 NetID (NetID and DOB only) */
-	public function credentialCheck_Loa1Valid() {
-		$result = self::$client->credentialCheck(
+	public function checkLOA_Loa1Valid() {
+		$result = self::$client->checkLOA(
 			self::$testData['loa1-user']['uid'],
 			new DateTime( self::$testData['loa1-user']['birthdate'] )
 		);
-		$this->assertTrue( $result );
+		$this->assertTrue( $result->getIsValid() );
 	}
 	
 	/** @test */
-	public function credentialCheck_invalidIT() {
-		$result = self::$client->credentialCheck(
+	public function checkLOA_invalid() {
+		$result = self::$client->checkLOA(
 				self::$testData['invalid-user']['uid'],
 				new DateTime( self::$testData['invalid-user']['birthdate'] ),
 				intval( self::$testData['invalid-user']['wiscard'] ) );
-		$this->assertFalse( $result );
+		$this->assertFalse( $result->getIsValid() );
+	}
+	
+	/** @test Returns invalid if LOA2 and no wiscard provided */
+	public function checkLOA_Loa2_no_wiscard() {
+		$result = self::$client->checkLOA(
+				self::$testData['user']['uid'],
+				new DateTime( self::$testData['user']['birthdate'] ) );
+		$this->assertFalse( $result->getIsValid() );
+		$this->assertContains( RpcNetidStructValidationResponse::REASON_NEEDS_WISCARD, $result->getReasons() );
+	}
+	
+	/** @test returns valid if LOA1 enters wiscard even if one doesn't exist */
+	public function checkLOA_loa1_with_wiscard_returns_valid() {
+		$result = self::$client->checkLOA(
+				self::$testData['loa1-user']['uid'],
+				new DateTime( self::$testData['loa1-user']['birthdate'] ),
+				intval( 12345678901 )
+		);
+		$this->assertTrue( $result->getIsValid() );
 	}
 	
 	/** @test */
diff --git a/src/test/resources/rpctest-netid.wsdl b/src/test/resources/rpctest-netid.wsdl
index 8209b5294d3250ef44409a83bb448e98e575a2e8..f8987db22bd8512bd24bd7d95551e663e172a2b8 100644
--- a/src/test/resources/rpctest-netid.wsdl
+++ b/src/test/resources/rpctest-netid.wsdl
@@ -33,6 +33,29 @@ targetNamespace="http://rpc.services.wisc.edu/UWDS/WebService/NetID">
 <xsd:complexType name="FaultType"><xsd:sequence>
 <xsd:element name="faultcode" type="xsd:QName"/><xsd:element name="faultstring" type="xsd:string"/><xsd:element name="faultactor" type="xsd:anyURI" minOccurs="0"/>
 </xsd:sequence></xsd:complexType>
+<xsd:element name="checkLOA">
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element minOccurs="1" maxOccurs="1" name="uid" type="xsd:string"/>
+                <xsd:element minOccurs="0" maxOccurs="1" name="cardid" type="xsd:string"/>
+                <xsd:element minOccurs="1" maxOccurs="1" name="birthdate" type="xsd:string"/>
+            </xsd:sequence>
+        </xsd:complexType>
+
+
+</xsd:element>
+<xsd:element name="checkLOAResponse">
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element minOccurs="1" maxOccurs="1" name="result" type="xsd:integer"/>
+            </xsd:sequence>
+        </xsd:complexType>
+
+</xsd:element>
+<xsd:element name="checkLOAFault">
+<xsd:complexType><xsd:sequence><xsd:element name="Fault" type="tns:FaultType"/></xsd:sequence></xsd:complexType>
+</xsd:element>
 <xsd:element name="Authenticate">
         <xsd:complexType>
             <xsd:sequence>
@@ -85,7 +108,7 @@ targetNamespace="http://rpc.services.wisc.edu/UWDS/WebService/NetID">
                 <xsd:element minOccurs="0" maxOccurs="1" name="system" type="xsd:string"/>
                 <xsd:element minOccurs="0" maxOccurs="1" name="domain" type="xsd:string"/>
                 <xsd:element minOccurs="1" maxOccurs="1" name="uid" type="xsd:string"/>
-                <xsd:element minOccurs="1" maxOccurs="1" name="cardid" type="xsd:string"/>
+                <xsd:element minOccurs="0" maxOccurs="1" name="cardid" type="xsd:string"/>
                 <xsd:element minOccurs="1" maxOccurs="1" name="birthdate" type="xsd:string"/>
             </xsd:sequence>
         </xsd:complexType>
@@ -625,6 +648,28 @@ targetNamespace="http://rpc.services.wisc.edu/UWDS/WebService/NetID">
 <xsd:element name="reserveNetIDFault">
 <xsd:complexType><xsd:sequence><xsd:element name="Fault" type="tns:FaultType"/></xsd:sequence></xsd:complexType>
 </xsd:element>
+<xsd:element name="setQuestions">
+        <xsd:complexType> 
+            <xsd:sequence>
+                <xsd:element minOccurs="1" maxOccurs="1" name="uid" type="xsd:string"/>
+                <xsd:element minOccurs="1" maxOccurs="1" name="Questions" type="tns:Questions"/> 
+            </xsd:sequence>
+        </xsd:complexType> 
+        
+
+</xsd:element>
+<xsd:element name="setQuestionsResponse">
+        
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element minOccurs="1" maxOccurs="1" name="result" type="xsd:integer"/>
+            </xsd:sequence>
+        </xsd:complexType>
+
+</xsd:element>
+<xsd:element name="setQuestionsFault">
+<xsd:complexType><xsd:sequence><xsd:element name="Fault" type="tns:FaultType"/></xsd:sequence></xsd:complexType>
+</xsd:element>
 <xsd:element name="reserveNetIDsTest">
 
         <xsd:complexType>
@@ -652,28 +697,6 @@ targetNamespace="http://rpc.services.wisc.edu/UWDS/WebService/NetID">
 <xsd:element name="reserveNetIDsTestFault">
 <xsd:complexType><xsd:sequence><xsd:element name="Fault" type="tns:FaultType"/></xsd:sequence></xsd:complexType>
 </xsd:element>
-<xsd:element name="setQuestions">
-        <xsd:complexType> 
-            <xsd:sequence>
-                <xsd:element minOccurs="1" maxOccurs="1" name="uid" type="xsd:string"/>
-                <xsd:element minOccurs="1" maxOccurs="1" name="Questions" type="tns:Questions"/> 
-            </xsd:sequence>
-        </xsd:complexType> 
-        
-
-</xsd:element>
-<xsd:element name="setQuestionsResponse">
-        
-        <xsd:complexType>
-            <xsd:sequence>
-                <xsd:element minOccurs="1" maxOccurs="1" name="result" type="xsd:integer"/>
-            </xsd:sequence>
-        </xsd:complexType>
-
-</xsd:element>
-<xsd:element name="setQuestionsFault">
-<xsd:complexType><xsd:sequence><xsd:element name="Fault" type="tns:FaultType"/></xsd:sequence></xsd:complexType>
-</xsd:element>
 <xsd:element name="testMethod">
   
         <xsd:complexType>
@@ -762,6 +785,20 @@ targetNamespace="http://rpc.services.wisc.edu/UWDS/WebService/NetID">
 </xsd:element>
 </xsd:schema>
 </wsdl:types>
+<wsdl:message name="checkLOAInput">
+<wsdl:part name="parameters"
+element="tns:checkLOA"/>
+</wsdl:message>
+
+<wsdl:message name="checkLOAOutput">
+<wsdl:part name="parameters"
+element="tns:checkLOAResponse"/>
+</wsdl:message>
+<wsdl:message name="checkLOAFault">
+<wsdl:part name="parameters"
+element="tns:checkLOAFault"/>
+</wsdl:message>
+
 <wsdl:message name="AuthenticateInput">
 <wsdl:part name="parameters"
 element="tns:Authenticate"/>
@@ -1112,32 +1149,32 @@ element="tns:reserveNetIDResponse"/>
 element="tns:reserveNetIDFault"/>
 </wsdl:message>
 
-<wsdl:message name="reserveNetIDsTestInput">
+<wsdl:message name="setQuestionsInput">
 <wsdl:part name="parameters"
-element="tns:reserveNetIDsTest"/>
+element="tns:setQuestions"/>
 </wsdl:message>
 
-<wsdl:message name="reserveNetIDsTestOutput">
+<wsdl:message name="setQuestionsOutput">
 <wsdl:part name="parameters"
-element="tns:reserveNetIDsTestResponse"/>
+element="tns:setQuestionsResponse"/>
 </wsdl:message>
-<wsdl:message name="reserveNetIDsTestFault">
+<wsdl:message name="setQuestionsFault">
 <wsdl:part name="parameters"
-element="tns:reserveNetIDsTestFault"/>
+element="tns:setQuestionsFault"/>
 </wsdl:message>
 
-<wsdl:message name="setQuestionsInput">
+<wsdl:message name="reserveNetIDsTestInput">
 <wsdl:part name="parameters"
-element="tns:setQuestions"/>
+element="tns:reserveNetIDsTest"/>
 </wsdl:message>
 
-<wsdl:message name="setQuestionsOutput">
+<wsdl:message name="reserveNetIDsTestOutput">
 <wsdl:part name="parameters"
-element="tns:setQuestionsResponse"/>
+element="tns:reserveNetIDsTestResponse"/>
 </wsdl:message>
-<wsdl:message name="setQuestionsFault">
+<wsdl:message name="reserveNetIDsTestFault">
 <wsdl:part name="parameters"
-element="tns:setQuestionsFault"/>
+element="tns:reserveNetIDsTestFault"/>
 </wsdl:message>
 
 <wsdl:message name="testMethodInput">
@@ -1197,6 +1234,32 @@ element="tns:releaseHeldLHSFault"/>
 </wsdl:message>
 
 <wsdl:portType name="NetIDPortType">
+<wsdl:operation name="checkLOA">
+
+<!--
+    
+        verifies a date of birth and a campus/photoid. 
+        
+        The response document consists of a result code.
+
+        Result Codes:
+
+                200 - credentials good.
+		400 - credentials bad.
+		401 - invalid input parameters.
+		402 - no PVI found for uid  
+		403 - no LOA found for uid 
+		404 - no Wiscard eligibility data found for uid 
+                405 - person is LOA2, missing Wiscard as input
+		500 - server error
+    
+
+-->
+<wsdl:input message="tns:checkLOAInput"/>
+<wsdl:output message="tns:checkLOAOutput"/>
+<wsdl:fault name="checkLOAFault"
+message="tns:checkLOAFault"/>
+</wsdl:operation>
 <wsdl:operation name="Authenticate">
 
 <!--
@@ -1590,6 +1653,7 @@ message="tns:deleteIssuedLHSFault"/>
         Result Codes:
                 
                 401 - bad args
+                422 - invalid email format
                 501 - Error Calling Oracle Function 
                 502 - setRecoveryEmail for $netid Failed 
 
@@ -1775,6 +1839,27 @@ message="tns:getNetIDForRecoveryEmailFault"/>
 <wsdl:fault name="reserveNetIDFault"
 message="tns:reserveNetIDFault"/>
 </wsdl:operation>
+<wsdl:operation name="setQuestions">
+
+<!--
+  
+        Sets the password recovery questions for a UDS person.    
+        
+        The response document consists of a result code.
+        
+        Result Codes:
+                
+                200 - OK
+                401 - bad parameters
+                500 - oracle error 
+        
+
+-->
+<wsdl:input message="tns:setQuestionsInput"/>
+<wsdl:output message="tns:setQuestionsOutput"/>
+<wsdl:fault name="setQuestionsFault"
+message="tns:setQuestionsFault"/>
+</wsdl:operation>
 <wsdl:operation name="reserveNetIDsTest">
 
 <!--
@@ -1798,27 +1883,6 @@ message="tns:reserveNetIDFault"/>
 <wsdl:fault name="reserveNetIDsTestFault"
 message="tns:reserveNetIDsTestFault"/>
 </wsdl:operation>
-<wsdl:operation name="setQuestions">
-
-<!--
-  
-        Sets the password recovery questions for a UDS person.    
-        
-        The response document consists of a result code.
-        
-        Result Codes:
-                
-                200 - OK
-                401 - bad parameters
-                500 - oracle error 
-        
-
--->
-<wsdl:input message="tns:setQuestionsInput"/>
-<wsdl:output message="tns:setQuestionsOutput"/>
-<wsdl:fault name="setQuestionsFault"
-message="tns:setQuestionsFault"/>
-</wsdl:operation>
 <wsdl:operation name="testMethod">
 
 <!--
@@ -1907,6 +1971,19 @@ message="tns:releaseHeldLHSFault"/>
 type="tns:NetIDPortType">
 <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
 style="document"/>
+<wsdl:operation name="checkLOA">
+<soap:operation soapAction="http://rpc.services.wisc.edu/UWDS/WebService/NetID#checkLOA"/>
+<wsdl:input>
+<soap:body use="literal"/>
+</wsdl:input>
+<wsdl:output>
+<soap:body use="literal"/>
+</wsdl:output>
+<wsdl:fault name="checkLOAFault">
+<soap:fault use="literal"
+name="checkLOAFault"/>
+</wsdl:fault>
+</wsdl:operation>
 <wsdl:operation name="Authenticate">
 <soap:operation soapAction="http://rpc.services.wisc.edu/UWDS/WebService/NetID#Authenticate"/>
 <wsdl:input>
@@ -2232,30 +2309,30 @@ name="getNetIDForRecoveryEmailFault"/>
 name="reserveNetIDFault"/>
 </wsdl:fault>
 </wsdl:operation>
-<wsdl:operation name="reserveNetIDsTest">
-<soap:operation soapAction="http://rpc.services.wisc.edu/UWDS/WebService/NetID#reserveNetIDsTest"/>
+<wsdl:operation name="setQuestions">
+<soap:operation soapAction="http://rpc.services.wisc.edu/UWDS/WebService/NetID#setQuestions"/>
 <wsdl:input>
 <soap:body use="literal"/>
 </wsdl:input>
 <wsdl:output>
 <soap:body use="literal"/>
 </wsdl:output>
-<wsdl:fault name="reserveNetIDsTestFault">
+<wsdl:fault name="setQuestionsFault">
 <soap:fault use="literal"
-name="reserveNetIDsTestFault"/>
+name="setQuestionsFault"/>
 </wsdl:fault>
 </wsdl:operation>
-<wsdl:operation name="setQuestions">
-<soap:operation soapAction="http://rpc.services.wisc.edu/UWDS/WebService/NetID#setQuestions"/>
+<wsdl:operation name="reserveNetIDsTest">
+<soap:operation soapAction="http://rpc.services.wisc.edu/UWDS/WebService/NetID#reserveNetIDsTest"/>
 <wsdl:input>
 <soap:body use="literal"/>
 </wsdl:input>
 <wsdl:output>
 <soap:body use="literal"/>
 </wsdl:output>
-<wsdl:fault name="setQuestionsFault">
+<wsdl:fault name="reserveNetIDsTestFault">
 <soap:fault use="literal"
-name="setQuestionsFault"/>
+name="reserveNetIDsTestFault"/>
 </wsdl:fault>
 </wsdl:operation>
 <wsdl:operation name="testMethod">