Skip to content

Commit

Permalink
feat(POM-276): Adyen 3DS integration example (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalii-vanziak-cko authored Oct 12, 2023
1 parent 62f2b45 commit 0b7c139
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 3 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ ext {
commonMarkVersion = '0.21.0'

checkout3dsSdkVersion = '3.1.1'
adyen3dsSdkVersion = '2.2.15'

junitVersion = '4.13.2'
mockitoVersion = '5.5.0'
Expand Down
2 changes: 2 additions & 0 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ dependencies {
implementation project(path: ':checkout-3ds')
implementation project(path: ':ui-core')

implementation "com.adyen.threeds:adyen-3ds2:$adyen3dsSdkVersion"

implementation "androidx.core:core-ktx:$androidxCoreVersion"
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.constraintlayout:constraintlayout:$androidxConstraintLayoutVersion"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.processout.example.ui.shared
package com.processout.example.service

import android.app.Activity
import android.net.Uri
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.processout.example.service

import android.app.Activity
import com.adyen.threeds2.*
import com.adyen.threeds2.parameters.ChallengeParameters
import com.adyen.threeds2.util.AdyenConfigParameters
import com.processout.sdk.api.model.threeds.PO3DS2AuthenticationRequest
import com.processout.sdk.api.model.threeds.PO3DS2Challenge
import com.processout.sdk.api.model.threeds.PO3DS2Configuration
import com.processout.sdk.api.model.threeds.PO3DSRedirect
import com.processout.sdk.api.service.PO3DSService
import com.processout.sdk.core.POFailure
import com.processout.sdk.core.ProcessOutResult
import com.processout.sdk.ui.threeds.PO3DSRedirectCustomTabLauncher

class POAdyen3DSService(
private val activity: Activity,
private val customTabLauncher: PO3DSRedirectCustomTabLauncher,
private val returnUrl: String
) : PO3DSService {

private var transaction: Transaction? = null

override fun authenticationRequest(
configuration: PO3DS2Configuration,
callback: (ProcessOutResult<PO3DS2AuthenticationRequest>) -> Unit
) {
try {
val adyenConfiguration = AdyenConfigParameters.Builder(
configuration.directoryServerId,
configuration.directoryServerPublicKey,
null // directoryServerRootCertificates
).build()

ThreeDS2Service.INSTANCE.initialize(
activity,
adyenConfiguration,
// Optional properties.
null, // locale
null // uiCustomization
)

transaction = ThreeDS2Service.INSTANCE.createTransaction(
null, // directoryServerID
configuration.messageVersion
).also {
val authenticationRequest = it.authenticationRequestParameters.toAuthenticationRequest()
callback(ProcessOutResult.Success(authenticationRequest))
}
} catch (e: Exception) {
// Handle specific Adyen exceptions.
callback(ProcessOutResult.Failure(POFailure.Code.Generic(), cause = e))
}
}

private fun AuthenticationRequestParameters.toAuthenticationRequest() =
PO3DS2AuthenticationRequest(
deviceData = deviceData,
sdkAppId = sdkAppID,
sdkEphemeralPublicKey = sdkEphemeralPublicKey,
sdkReferenceNumber = sdkReferenceNumber,
sdkTransactionId = sdkTransactionID
)

override fun handle(
challenge: PO3DS2Challenge,
callback: (ProcessOutResult<Boolean>) -> Unit
) {
val challengeParameters = ChallengeParameters().apply {
set3DSServerTransactionID(challenge.threeDSServerTransactionId)
acsTransactionID = challenge.acsTransactionId
acsRefNumber = challenge.acsReferenceNumber
acsSignedContent = challenge.acsSignedContent
}

val challengeStatusHandler = ChallengeStatusHandler { challengeResult ->
when (challengeResult) {
is ChallengeResult.Completed -> {
val status = challengeResult.transactionStatus.uppercase() == "Y"
callback(ProcessOutResult.Success(status))
}
is ChallengeResult.Cancelled -> callback(
ProcessOutResult.Failure(
POFailure.Code.Cancelled,
challengeResult.transactionStatus
)
)
is ChallengeResult.Timeout -> callback(
ProcessOutResult.Failure(
POFailure.Code.Timeout(),
challengeResult.transactionStatus
)
)
is ChallengeResult.Error -> callback(
ProcessOutResult.Failure(
POFailure.Code.Generic(),
challengeResult.transactionStatus
)
)
}
}

try {
transaction?.doChallenge(
activity,
challengeParameters,
challengeStatusHandler,
5 // Timeout in minutes.
) ?: run {
ThreeDS2Service.INSTANCE.cleanup(activity)
callback(
ProcessOutResult.Failure(
POFailure.Code.Internal(),
"Cannot perform challenge. Transaction is not initialized."
)
)
}
} catch (e: Exception) {
// Handle specific Adyen exceptions.
callback(ProcessOutResult.Failure(POFailure.Code.Generic(), cause = e))
}
}

override fun handle(redirect: PO3DSRedirect, callback: (ProcessOutResult<String>) -> Unit) {
customTabLauncher.launch(redirect, returnUrl, callback)
}

override fun cleanup() {
transaction?.let {
it.close()
ThreeDS2Service.INSTANCE.cleanup(activity)
transaction = null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import com.checkout.threeds.Environment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.processout.example.R
import com.processout.example.databinding.FragmentCardPaymentBinding
import com.processout.example.service.Checkout3DSServiceDelegate
import com.processout.example.service.POAdyen3DSService
import com.processout.example.shared.Constants
import com.processout.example.shared.onFailure
import com.processout.example.shared.onSuccess
Expand All @@ -22,7 +24,6 @@ import com.processout.example.ui.screen.card.CardPaymentUiState.Authorizing
import com.processout.example.ui.screen.card.CardPaymentUiState.Failure
import com.processout.example.ui.screen.card.CardPaymentUiState.Submitted
import com.processout.example.ui.screen.card.CardPaymentUiState.Submitting
import com.processout.example.ui.shared.Checkout3DSServiceDelegate
import com.processout.sdk.api.ProcessOut
import com.processout.sdk.api.model.request.POInvoiceAuthorizationRequest
import com.processout.sdk.api.service.PO3DSService
Expand Down Expand Up @@ -57,7 +58,7 @@ class CardPaymentFragment : BaseFragment<FragmentCardPaymentBinding>(
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
invoices.authorizeInvoiceResult.collect { onAuthorizeInvoiceResult(it) }
}
}
Expand Down Expand Up @@ -88,6 +89,7 @@ class CardPaymentFragment : BaseFragment<FragmentCardPaymentBinding>(
}
return when (selected3DSService) {
getString(R.string.threeds_service_checkout) -> createCheckout3DSService()
getString(R.string.threeds_service_adyen) -> createAdyen3DSService()
else -> createTest3DSService()
}
}
Expand All @@ -111,6 +113,13 @@ class CardPaymentFragment : BaseFragment<FragmentCardPaymentBinding>(
.with(environment = Environment.PRODUCTION)
.build()

private fun createAdyen3DSService(): PO3DSService =
POAdyen3DSService(
activity = requireActivity(),
customTabLauncher = customTabLauncher,
returnUrl = Constants.RETURN_URL
)

private fun setOnClickListeners() {
binding.authorizeInvoiceButton.setOnClickListener { onSubmitClick() }
binding.currencyInput.setOnEditorActionListener { _, actionId, _ ->
Expand Down
7 changes: 7 additions & 0 deletions example/src/main/res/layout/fragment_card_payment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@
android:layout_height="wrap_content"
android:text="@string/threeds_service_checkout"
android:textAppearance="@style/TextAppearance.ProcessOut.Fixed.Body" />

<RadioButton
android:id="@+id/threeds_service_adyen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/threeds_service_adyen"
android:textAppearance="@style/TextAppearance.ProcessOut.Fixed.Body" />
</RadioGroup>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
Expand Down
1 change: 1 addition & 0 deletions example/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<string name="threeds_service" translatable="false">3DS Service</string>
<string name="threeds_service_test" translatable="false">POTest3DSService</string>
<string name="threeds_service_checkout" translatable="false">POCheckout3DSService</string>
<string name="threeds_service_adyen" translatable="false">POAdyen3DSService</string>

<string name="ok" translatable="false">OK</string>
<string name="success" translatable="false">Success</string>
Expand Down

0 comments on commit 0b7c139

Please sign in to comment.