Adyen / adyen-ios

Adyen iOS Drop-in and Components
https://docs.adyen.com/checkout/ios
MIT License
152 stars 122 forks source link

[NEED ASSIST] Testing the TWINT native integration on iOS #1783

Closed elitalon closed 1 week ago

elitalon commented 3 months ago

Describe the challenge I'm trying to test the native integration of TWINT on an iOS app, following the steps in AdyentTwint documentation and targeting Adyen's Test environment.

I have installed TWINT's Configurator app, with the environment set to INT.

Our app is configured with all 50 twint-issuerN schemes and we are launching the drop-in as follows:

import Adyen
import AdyenDropIn
import AdyenTwint

/* … */

let context = try AdyenContext(
    apiContext: .init(environment: .test, clientKey: <redacted>),
    payment: .init(
        amount: .init(value: 0, currencyCode: "CHF", localeIdentifier: nil),
        countryCode: "CH"
    )
)

let configuration = DropInComponent.Configuration(style: /* … */)
configuration.card.showsStorePaymentMethodField = false
configuration.actionComponent.twint = .init(callbackAppScheme: <redacted>)

let dropIn = DropInComponent(
    paymentMethods: paymentMethods, // This is a `PaymentMethods` instance from Adyen SDK
    context: context,
    configuration: configuration
)
dropIn.delegate = self
presentingViewController.present(dropIn.viewController, animated: true)

The problem is that after tapping on the Twint option in the drop-in, I don't think we get the native TWINT integration, but rather a webshop interface:

(1) Selection of TWINT (2) Web interface
IMG_6085 step_02

Is there anything else that needs to be configured?

Documentation update (optional) The guidelines in AdyentTwint documentation do not mention anything about the need of using TWINT's Configurator app to test the integration during development.

Screenshots Added above as part of the description of the issue.

goergisn commented 3 months ago

Hi @elitalon thanks for opening that separate issue. From what I see the code looks good. You can also double check with the implementation of the Demo App. I will check internally if there is any additional configuration needed and keep you posted.

elitalon commented 2 months ago

@goergisn Do you have any news on this? I checked the demo app and I don't see that we are doing anything significantly different.

I noticed that the documentation says that

After pressing the button on the component, TwintComponent makes a /payments call with subtype: sdk and trigger the flow with retrieving the code through ActionComponent

But if I inspect the requests made by the app, I don't see any trace of that subtype: sdk. Do we need to enable this explicitly?

goergisn commented 2 months ago

Hi @elitalon,

I assume you are using the SPM integration with Advanced Flow (Not the Session integration) right? If AdyenTwint is available (Which it seems based on your import statement) it should automatically use the "native" integration. Do you use any 3rd party build tool that might "mess" with included frameworks?


To double check an assumption, could you please add a breakpoint in following places of the Checkout SDK?

The AdyenTwint module contains the TwintSDK and thus should go the route of decoding as a TwintPaymentMethod instead of an AnyPaymentMethod and further build a TwintComponent that automatically sends the subtype: sdk.

Something must go wrong in one of the places.

Screenshot 2024-09-12 at 13 05 55 Screenshot 2024-09-12 at 13 08 06
nauaros commented 2 months ago

@elitalon

In addition to what @goergisn asked, can you clarify if:

  1. You're using sessions or advanced integration?
  2. Are you referencing TEST or LIVE environment (on Adyen)?
elitalon commented 2 months ago

@goergisn @nauaros I finally got a chance to look at this in more detail, apologies for the delay in my answer.

We're indeed using the advanced integration with the TEST environment.

As for the breakpoints, I set them in the places indicated and I've seen the following behaviour.

In TwintPaymentMethodDecoder (see the screenshot below), only decode(from:isStored:) -> AnyPaymentMethod is called in the "TWINT branch".

TwintPaymentMethodDecoder ![AnyPaymentMethodDecoder](https://github.com/user-attachments/assets/89ebaf33-7dfb-459a-9ec1-a908af1c533d)

The other method is never called, but I'm not sure that's the expected behaviour.

If I remove the AdyenTwint framework from the project, the second method is called again, just not on the TWINT branch (and thus returning nil).

In ComponentManager (see also the screenshot below), only build(paymentMethod:) -> PaymentComponent? is also called in the "TWINT branch".

ComponentManager ![ComponentManager](https://github.com/user-attachments/assets/776466bd-5fd5-4ea3-b79c-2e1293190b74)

As far as I know, we don't have any other third-party build tool interfering with frameworks in build phases.

What we have though is a post-action build step in the target's scheme to account for a known bug in Xcode:

# https://developer.apple.com/documentation/xcode-release-notes/xcode-12_4-release-notes FB8761306
rm -rf "${TARGET_BUILD_DIR}/${TARGET_NAME}.app"/PlugIns/*.framework

I hope that gives you a hint about what could be wrong.

Thanks!

elitalon commented 2 months ago

Something I forgot in my previous comment is that these messages also appear in the logs of our app:

objc[4245]: Class TWAppLinkDataValidator is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a61e0) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e060). One of the two will be used. Which one is undefined.
objc[4245]: Class TWAppLinkData is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a6230) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e0b0). One of the two will be used. Which one is undefined.
objc[4245]: Class TWAppChooserNetworking is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a6258) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e0d8). One of the two will be used. Which one is undefined.
objc[4245]: Class TWInstalledAppFetcher is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a62a8) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e128). One of the two will be used. Which one is undefined.
objc[4245]: Class TWError is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a6320) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e1a0). One of the two will be used. Which one is undefined.
objc[4245]: Class TWAppConfiguration is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a6348) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e1c8). One of the two will be used. Which one is undefined.
objc[4245]: Class Twint is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a63c0) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e240). One of the two will be used. Which one is undefined.
objc[4245]: Class TWAppChooserController is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a6410) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e290). One of the two will be used. Which one is undefined.
objc[4245]: Class TWAppLinkDataTransformer is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a6460) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e2e0). One of the two will be used. Which one is undefined.
objc[4245]: Class TWAppLinkToTwintAdapter is implemented in both /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/Datatrans.framework/Datatrans (0x1067a64b0) and /private/var/containers/Bundle/Application/B99E38A7-493B-4A17-BD3D-C002C9FA3A6F/Fairtiq.app/Frameworks/TwintSDK.framework/TwintSDK (0x10517e330). One of the two will be used. Which one is undefined.

Our app also integrates Datatrans' SDK, which includes TwintSDK. However, although not desirable, this shouldn't be a problem because the OS will pick one.

I'm saying this based in our current experience with other frameworks that have worked fine so far. But there's maybe something particular about TwintSDK that invalidates this assumption.

goergisn commented 2 months ago

Hi @elitalon this already helps a lot 👍 We can now confirm that the SDK detects the TwintSDK and goes the right route.

Now we have to make sure that you are sending the correct data to your (proxy) backend so it can send the correct data to the /payments endpoint. You mentioned that you don't see any request from the app containing a subtype: sdk so it might be that there is something off in the way you construct the data sent to the backend. Is there a way you can provide the request/information you're sending? (The TwintComponent creates a PaymentComponentData object containing the TwintDetails this object has to be provided as is)

elitalon commented 2 months ago

Hey @goergisn, I verified the request sent by the app and the subtype field is now being sent to our proxy backend. I wonder what has changed since I open this issue (a month ago 🙈).

In any case we still see the web based UI, not the native one. So we are going to check now how our proxy handles this request. Chances are, Java serialisation is dropping this subtype field during deserialisation because it's not explicitly declared.

I'll keep you posted, thanks a lot for the support so far.

elitalon commented 2 months ago

@goergisn It looks like the request ends up being deserialised into a StoredPaymentMethodDetails object in our proxy backend implementation. But as far as I can see, this class doesn't contain any subtype member.

Is there anything additional we have to configure so that the correct type (or subtype) gets used?

For reference, we are currently using the version 28.2.0 of the Java API library.

goergisn commented 2 months ago

Hi @elitalon it looks like this is a limitation of the Java API Library do you mind creating an issue there? I will also check with our backend developers to get some more information.

elitalon commented 2 months ago

@goergisn There you go: https://github.com/Adyen/adyen-java-api-library/issues/1360

goergisn commented 1 month ago

Thank you for reporting and sorry for the inconvenience. I'll leave the ticket open for visibility as well for now.

seanlabastille commented 1 month ago

@goergisn I am sorry for hijacking this thread but we have recently become aware of an issue with using the TWINT SDK if the app was built using Xcode 16 (links to the TWINT apps can't be opened). A fix is expected within the next days, but this means you will probably need to ship an update of your integration before we can fully resolve this.

nauaros commented 1 month ago

Hi @seanlabastille - Are you building with iOS 18?

seanlabastille commented 1 month ago

@nauaros We are building with Xcode 16 so the default SDK is iOS 18. A fix from TWINT should be available now.

goergisn commented 1 week ago

Closing the ticket as an update was released to the java library. Feel free to re-open if there are any other questions.

There will be a release with the new Twint SDK soon as well.

elitalon commented 1 week ago

Thanks @goergisn, I'll keep you posted if we find something else.