> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/nfc-wallet-sdk-android/implement-nfc-wallet/make-payment/implement-contactless-payments/5.-perform-cdcvm-verification.md).

# 5. Perform CDCVM verification

## Overview

During a contactless transaction, NFC Wallet SDK can require the **end user** to authenticate to complete CDCVM verification.

Your **digital wallet application** must perform this authentication using the CDCVM method you configured earlier. See [Set CDCVM method](/nfc-wallet-sdk-android/implement-nfc-wallet/tokenize-a-card/set-cdcvm-method.md).

## SDK integration

Handle authentication in `ContactlessPaymentServiceListener.onAuthenticationRequired()`. See [Implement contactless payment callbacks](/nfc-wallet-sdk-android/implement-nfc-wallet/make-payment/implement-contactless-payments/2.-implement-contactless-payment-callbacks.md).

To authenticate the end user, use the `CHVerificationMethod` provided by the SDK. Use it to obtain a `DeviceCVMVerifier` instance, then start authentication and listen for the result using `DeviceCVMVerifyListener`.

`cvmResetTimeout` tells you how long the verification stays valid. Use it to guide the end user for the second tap.

### CDCVM verification with device keyguard

When the device keyguard is used as the CDCVM method:

```java
//From a ContactlessPaymentServiceListener() implementation
//...

@Override
public void onAuthenticationRequired(
  PaymentService activatedPaymentService,
  CHVerificationMethod cvm,
  long cvmResetTimeout) {

    // check the CDCVM type
    if(cvm == CHVerificationMethod.DEVICE_KEYGUARD) {
        // Launch the Activity implemented to manage the Keyguard authentication screen
        // In this example the activity is called 'KeyguardActivity'
        Intent intent = new Intent(getApplicationContext(), KeyguardActivity.class);
        intent.putExtra(Tags.CVM, cvm);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

//...
```

Implement `KeyguardActivity` in your digital wallet application. It must extend `DeviceCVMKeyguardActivity`. This enables CDCVM verification using device credentials.

The following example shows the implementation of the `KeyguardActivity` class:

{% code expandable="true" %}

```java
public class KeyguardActivity extends DeviceCVMKeyguardActivity{

     private static String TAG = KeyguardActivity.class.getName();
     private PaymentBusinessService paymentBusinessService;
     private DeviceCVMVerifier chDeviceCVMVerifier;
     private TextView message;
     private boolean keyguardVerificationStart = false;
     private CharSequence title;
     private CharSequence message1;

     @Override
     protected void onCreate(Bundle savedInstanceState) {

      Log.d(TAG, "KeyguardActivity:onCreate");
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_device_keyguard);
      Log.d(TAG, "KeyguardActivity:unlockAndWake : start");
      unlockAndWake();
      Log.d(TAG, "KeyguardActivity:unlockAndWake : end");

      title=getString(R.string.keyguard_title);
      message1=getString(R.string.keyguard_message);

      Bundle extras = getIntent().getExtras();

      // get the cvm object from the bundle passed
      CHVerificationMethod cvm = (CHVerificationMethod) extras.getSerializable(Tags.CVM);

      message = (TextView) findViewById(R.id.message);

      paymentBusinessService = PaymentBusinessManager.getPaymentBusinessService();

      PaymentService paymentService = paymentBusinessService.getActivatedPaymentService();

      // thanks to the cvm object, get an instance of the corresponding DeviceCVMVerifier object
      chDeviceCVMVerifier = (DeviceCVMVerifier) paymentService.getCHVerifier(cvm);

      // set then the corresponding listener to the DeviceCVMVerifier object
      chDeviceCVMVerifier.setDeviceCVMVerifyListener(new DeviceCVMVerifyListener() {

      @Override
      public void onVerifySuccess() {
         // Verification was OK!
         message.setText("");
         KeyguardActivity.this.finish();
     }


      @Override
      public void onVerifyError(int errorCode, CharSequence charSequence) {
          //Not expected to be called
      }

      @Override
      public void onVerifyFailed() {
        // Verification is Not OK! User shall be asked to retry
        message.setText("Unable to autheticate. Please try again.");
      }


      @Override
      public void onVerifyHelp(int i, CharSequence charSequence) {
          //Not expected to be called
      }

      });

      cbDeviceCVMVerifier.setKeyguardActivity(this);

      //Start Authentication when the screen is ON and Unlock
      if (DeviceUtil.isDeviceScreenOn(getApplicationContext())) {

             Log.d(TAG, "Starting device keyguard authentication");
             keyguardVerificationStart = true;
             DeviceCVMVerifierInput input = new  DeviceCVMVerifierInput(title,message1);
             chDeviceCVMVerifier.startAuthentication(input);
       } else {
             Log.d(TAG, "Screen is disactivate, skiping keyguard authentication");
       }

     }

}
```

{% endcode %}

### CDCVM verification with biometrics (fingerprint example)

Use this method when the device supports biometric authentication (fingerprint is shown in the example).

The following example shows how this mechanism can be implemented:

```java
//From a ContactlessPaymentServiceListener() implementation
//...

@Override
public void onAuthenticationRequired(
  PaymentService activatedPaymentService,
  CHVerificationMethod cvm,
  long cvmResetTimeout) {

    // check the CDCVM type
    if(cvm == CHVerificationMethod.FINGERPRINT) {
        // Launch the  activity that manages the Fingerprint authentication screen
        // In this case this activity is called 'BioFingerprintActivity'
        Intent intent = new Intent(getApplicationContext(),
        BioFingerprintActivity.class);
        intent.putExtra(Tags.CVM, cvm);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

//...
```

Implement a dedicated `Activity` that extends `DeviceCVMKeyguardActivity`. This enables fallback to device keyguard when biometric verification fails.

{% code expandable="true" %}

```java
public class BioFingerprintActivity extends DeviceCVMKeyguardActivity {

    private static String TAG = BioFingerprintActivity.class.getName();
    private PaymentBusinessService paymentBusinessService;
    private DeviceCVMVerifier deviceCVMVerifier;
    private CancellationSignal cancellationSignal;
    private TextView message;
    private boolean isBioFPVerificationStarted = false;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_bio_fingerprint_2);
      unlockAndWake();
      Bundle extras = getIntent().getExtras();

      // get the cvm object passed through the bundle
      CHVerificationMethod cvm = (CHVerificationMethod) extras.getSerializable(Tags.CVM);

      message = (TextView) findViewById(R.id.message);
      paymentBusinessService = PaymentBusinessManager.getPaymentBusinessService();
      PaymentService paymentService = paymentBusinessService.getActivatedPaymentService();

      // thanks to the cvm object get an intance to the 'DeviceCVMVerifier'
      deviceCVMVerifier = (DeviceCVMVerifier) paymentService.getCHVerifier(cvm);

			// set the corresponding listener
      deviceCVMVerifier.setDeviceCVMVerifyListener(new DeviceCVMVerifyListener() {

            @Override
            public void onVerifySuccess() {
              // Verification is OK!
              message.setText("");
              BioFingerprintActivity.this.finish();
            }

            @Override
            public void onVerifyError(int errorCode, CharSequence charSequence) {

              Log.d(TAG, "BioFingerprintActivity:error :" + errorCode);

              // Special case when triggered in lock screen mode
              // FINGERPRINT_ERROR_CANCELED error is raised
              if (errorCode == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
                  Log.d(TAG, "restart Fp");
                  if (cancellationSignal != null)
                      cancellationSignal.cancel();

              isBioFPVerificationStarted=true;
              cancellationSignal = new CancellationSignal();
              DeviceCVMVerifierInput input = new DeviceCVMVerifierInput(cancellationSignal);
              deviceCVMVerifier.startAuthentication(input);
              }
              // usual handling of error code
              else {
                        message.setText(charSequence + ". Please try again...");
                        if(errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
                            confirmCredential("Verify by Keyguard", "Too many attempts. Please verify  using your PIN / Pattern / Password");
                  }
              }
            }

            @Override
            public void onVerifyFailed() {
                message.setText("Unable to recognize the fingerprint. Please try again.");
            }


            @Override
            public void onVerifyHelp(int i, CharSequence charSequence) {
                message.setText(charSequence + ". Please try again.");
            }

        });

        deviceCVMVerifier.setKeyguardActivity(this);
        cancellationSignal = new CancellationSignal();

        //Start Authentication when the screen is ON and Unlock
        if (DeviceUtil.isDeviceScreenOn(getApplicationContext())) {
            Log.d(TAG, "Starting Fingerprint authentication");
            DeviceCVMVerifierInput input = new DeviceCVMVerifierInput(cancellationSignal);
            deviceCVMVerifier.startAuthentication(input);
        } else {
            Log.d(TAG, "Screen is de-activated, skipping Fingerprint authentication");
        }
    }

  	/* In case of fingerprint verification failure, implementing the following callback is possible
    to fallback on the Keyguard verification.
    **/
    public void onKeyguardFallback(View v) {

      Log.d(TAG, "onKeyguardFallback");
      // call deviceCVMVerifier.startAuthentication(input)


    }

  	@Override
  	public void onCancel(View v) {
      Log.d(TAG, "Cancel authentication");
      cancelTransaction("The transaction has been cancelled.");
    }

    @Override
    public void onBackPressed() {
        Log.d(TAG, "onBackPressed()");
        cancelTransaction("The transaction has been cancelled.");
    }


    /*
    After cancelling the fingerprint biometric authentication, the Payment service shall be deactivated
    **/
  	private void cancelTransaction(String message) {
          if (cancellationSignal != null) {
              cancellationSignal.cancel();
          }
          PaymentBusinessManager.getPaymentBusinessService().deactivate();
    }

}
```

{% endcode %}

If the digital wallet application goes to the background, stop listening for fingerprint authentication.

This can be accomplished using the following code snippet:

```java
@Override
public void onResume() {
       super.onResume();
       cancellationSignal=new CancellationSignal();
       deviceCVMVerifier.startAuthentication(cancellationSignal);
   }

@Override
public void onPause() {
     if(cancellationSignal != null)
             cancellationSignal.cancel();
}
```

### Support lock screen scenarios (optional)

If you need to support authentication prompts when the device is locked, verify the following:

* Activities shown during payment are declared with `showOnLockScreen=true`.
* A wake lock is acquired and window flags are set to show the UI on the lock screen.
* Release the `wakeLock` when the payment completes to reduce battery usage.

Configure the following settings in the manifest, and implement the sample code as needed:

```xml
<activity
        android:name=".BioFingerprintActivity"
        android:showOnLockScreen="true"
        android:screenOrientation="portrait" />
```

```java
@Override
public void onCreate() {
        //…
        unlockAndWake();
        //…
}

private void unlockAndWake() {

    PowerManager.WakeLock mWl;
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    if (!pm.isScreenOn()) {
        mWl = pm.newWakeLock(
                PowerManager.SCREEN_BRIGHT_WAKE_LOCK | 
                PowerManager.FULL_WAKE_LOCK | 
                PowerManager.ACQUIRE_CAUSES_WAKEUP, "");
        mWl.acquire();
    }

    getWindow().addFlags(
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | 
            WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON   | 
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON   | 
            WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}

@Override
public void onDestroy() {

  try{
      if(null!=mW1){
          mWl.release();
      }
  }
 	catch(Exception e){ }
}
```

### Delegated authentication

Delegated authentication lets your digital wallet application prompt the end user for authentication and then inform the SDK that the payment can proceed.

The authentication can use device keyguard or biometrics to unlock the underlying keystore protected by user authentication.

This flow applies when the SDK requests authentication during payment. Your digital wallet application can either:

* Prompt the end user for authentication, then call `DeviceCVMVerifier.onDelegatedAuthPerformed(timeOfAuth)`.
* Reuse a recent successful authentication (within the configured key validity period) and call `DeviceCVMVerifier.onDelegatedAuthPerformed(timeOfAuth)` immediately.

If the end user aborts the transaction, call `DeviceCVMVerifier.onDelegatedAuthCancelled()`.

```java
@Override
public void onAuthenticationRequired(final PaymentService service, CHVerificationMethod cvm, long cvmResetTimeout) {
                            
  // only applicable for Biometric or KeyGuard method
   if(cvm == CHVerificationMethod.BIOMETRICS || cvm == CHVerificationMethod.DEVICE_KEYGUARD){
      // obtain the verifier
      final DeviceCVMVerifier verifier = (DeviceCVMVerifier)service.getCHVerifier(cvm);
                                
      // MPA perform authentication, providing the time-stamp of the authentication
      ...

      // time stamp is used by SDK to compare against current time, to make sure that it is still within the Key-Validity-Duration

      // MPA could configure CVM Type as FINGERPRINT if verification method is biometrics
      verifier.setCVMType(CVMType.FINGERPRINT);

      // it is also recommended that MPA performs a check and ensure that there are enough time left for the end user to perform the 2nd tap of a transaction.
      verifier.onDelegatedAuthPerformed(timeOfAuth);
                                
      // if user does not wish to perform the authentication MPA shall cancel the transaction
      verifier.onDelegatedAuthCancelled();
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.payments.thalescloud.io/nfc-wallet-sdk-android/implement-nfc-wallet/make-payment/implement-contactless-payments/5.-perform-cdcvm-verification.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
