datatrans / android-sdk

Accept payments on your Android apps: Our mobile SDKs support your entire payment and registration process and simplify the integration of any payment method in your mobile apps.
Other
6 stars 0 forks source link

[3.1.1] Unable to match the desired swap behavior #11

Closed AmirSalahY closed 1 year ago

AmirSalahY commented 1 year ago

When triggering the Transaction function with the token it shows me this error Unable to match the desired swap behavior.

image

expectations: to show the forms correctly

richiehug commented 1 year ago

Hey @AmirSalahY - can you please share a mobileToken and how you invoke the SDK?

bacherma commented 1 year ago

Hi @AmirSalahY, thanks for the report. However, we need way more context to figure out what could cause your issue and to reproduce it. OS, simulator or device, hardware, dev environment, frameworks & tools used, what are you doing in the code exactly, and so on.

AmirSalahY commented 1 year ago
package com.reactnativedatatrans;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;

import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import ch.datatrans.payment.api.Transaction;
import ch.datatrans.payment.api.TransactionListener;
import ch.datatrans.payment.api.TransactionRegistry;
import ch.datatrans.payment.api.TransactionSuccess;
import ch.datatrans.payment.api.tokenization.PCIPCardInfo;
import ch.datatrans.payment.exception.TransactionException;
import ch.datatrans.payment.paymentmethods.Card;
import ch.datatrans.payment.paymentmethods.CardExpiryDate;
import ch.datatrans.payment.paymentmethods.PaymentMethodType;
import ch.datatrans.payment.paymentmethods.SavedCard;
import ch.datatrans.payment.paymentmethods.SavedPaymentMethod;

public class DatatransModule extends ReactContextBaseJavaModule{
    public static final String NAME = "Datatrans";
    private Context mActivityContext;
    private final String CALLBACK_TYPE_SUCCESS = "success";
    private final String CALLBACK_TYPE_ERROR = "error";
    private final String CALLBACK_TYPE_CANCEL = "cancel";
    private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
    private JSONArray jsonarray;
    private JSONObject jsonobject;
    private WritableMap mapObj;
    //private Callback mTokenCallback;
    private  Promise mPromise;

    public DatatransModule(ReactApplicationContext reactContext) {
        super(reactContext);
        // mActivityContext = activityContext;
    }

    @Override
    @NonNull
    public String getName() {
        return NAME;
    }
    @ReactMethod
    public void logData(String name, String text) {
        Log.d("CalendarModule", "Create event called with name: " + name
                + " and location: " + text);
    }

    // Example method
    // See https://reactnative.dev/docs/native-modules-android
    @ReactMethod
    public void transaction(String mobileToken, ReadableMap options, final Promise promise) {
        ReactApplicationContext context = getReactApplicationContext();
        Activity activity = getCurrentActivity();
        mPromise = promise;
        if (activity == null) {
            WritableMap map = Arguments.createMap();
            map.putString("message", "Activity doesn't exist");
            consumeCallback(E_ACTIVITY_DOES_NOT_EXIST, map);
            return;
        }

        //aliasPaymentMethods
        if (activity != null) {
            //Intent intent = new Intent(context, ExternalProcessRelayActivity.class);
            // activity.startActivity(intent);
            Transaction transaction;
            try {
                Collection paymentCollection= new ArrayList();

                ReadableArray aliasPaymentMethods;//=new ArrayList<>();
                aliasPaymentMethods=options.getArray("aliasPaymentMethods");
                //aliasPaymentMethods.get
                // List<PaymentMethodType> paymentMethodTypes = new ArrayList<>();
                List<SavedPaymentMethod> paymentMethodTypes = new ArrayList<>();
                //Log.d("myTag", aliasPaymentMethods.toString());
                for(int i = 0; i < aliasPaymentMethods.size(); i++)
                {
                    ReadableMap apm;
                    apm=aliasPaymentMethods.getMap(i);
                    //playersObjects[i] = new PlayerScores(); //make the object so we can access it
                    //playersObjects[i].playerNameSet(name variable);

                    //PaymentMethodType.valueOf(apm.getString("paymentMethods"));
                    // PaymentMethodType pmtype=new PaymentMethodType(aliasPaymentMethods.getString("paymentMethods"));
                    //pmtype=aliasPaymentMethods.getString("paymentMethods");
                    CardExpiryDate ced=new CardExpiryDate(apm.getInt("expiryMonth"),apm.getInt("expiryYear"));
                    //         PaymentMethodType.VISA.getIdentifier();
                    //  Log.d("Visssssss",PaymentMethodType.VISA.toString());
                    //  Log.d("fromIdentifier", PaymentMethodType.fromIdentifier(apm.getString("paymentMethods")).toString());
                    //  CardToken ct=new CardToken(PaymentMethodType.fromIdentifier(apm.getString("paymentMethods")),apm.getString("alias"),ced,apm.getString("ccNumber"),"");
                    SavedCard ct=new SavedCard(PaymentMethodType.fromIdentifier(apm.getString("paymentMethods")),apm.getString("alias"),ced,apm.getString("ccNumber"),"");
                    paymentMethodTypes.add(ct);
                    // SavedPaymentMethod spm = new SavedPaymentMethod(PaymentMethodType.fromIdentifier(apm.getString("paymentMethods")),apm.getString("alias"));
                    // paymentMethodTypes.add(spm);
                }

                //modules.add(new DatatransModule(reactContext));

                if(paymentMethodTypes.size()>0) {
                    transaction = new Transaction(mobileToken, (List<? extends SavedPaymentMethod>) paymentMethodTypes);
                }
                else{
                    transaction = new Transaction(mobileToken);
                }

                // transaction = new Transaction(mobileToken);
                TransactionListener transactionListener= new TransactionListener() {
                    @Override
                    public void onTransactionCancel() {
                        WritableMap map = Arguments.createMap();
                        WritableMap data = Arguments.createMap();

                        map.putMap("data",data);
                        map.putString("action","Cancel");

                        Toast.makeText(getReactApplicationContext(), "Cancel", Toast.LENGTH_LONG).show();
                        mapObj=map;
                        consumeCallback(CALLBACK_TYPE_CANCEL, map);
                    }

                    @Override
                    public void onTransactionError(@NotNull TransactionException e) {
                        WritableMap map = Arguments.createMap();
                        WritableMap data = Arguments.createMap();
                        data.putString("transactionId", e.getTransactionId());
                        data.putString("message", e.getMessage());
                        data.putString("paymentMethodType", e.getPaymentMethodType().getIdentifier());
                        map.putMap("data",data);
                        map.putString("action","Error");
                        Toast.makeText(getReactApplicationContext(), "Error", Toast.LENGTH_LONG).show();
                        mapObj=map;
                        consumeCallback(CALLBACK_TYPE_ERROR, map);
                    }

                    @Override
                    public void onTransactionSuccess(@NotNull TransactionSuccess transactionSuccess) {
                        WritableMap map = Arguments.createMap();
                        WritableMap data = Arguments.createMap();
                        data.putString("transactionId", transactionSuccess.getTransactionId());
                        data.putString("paymentMethodToken",""); //transactionSuccess.getPaymentMethodToken().toString()
                        data.putString("paymentMethodType", transactionSuccess.getPaymentMethodType().getIdentifier());

                        map.putMap("data",data);
                        map.putString("action","Finish");

                        Toast.makeText(getReactApplicationContext(), "Success", Toast.LENGTH_LONG).show();
                        mapObj=map;
                        consumeCallback(CALLBACK_TYPE_SUCCESS, map);
                    }
                };
                transaction.setListener(transactionListener); // this refers to Android's Activity
                transaction.getOptions().setAppCallbackScheme(options.getString("appCallbackScheme"));
                transaction.getOptions().setTesting(options.getBoolean("isTesting") );
                transaction.getOptions().setUseCertificatePinning(options.getBoolean("isUseCertificatePinning"));
                TransactionRegistry.INSTANCE.startTransaction(activity, transaction);

            } catch (Exception e) {

                promise.reject(e);
            }
        }

    }

    private void consumeCallback(String type, WritableMap map) {
        if (mPromise != null) {
            map.putString("type", type);
            map.putString("provider", "ct-datatrans");

            mPromise.resolve(map);
            mPromise = null;
        }
    }

    @ReactMethod
    public void multiply(int a, int b, Promise promise) {
        promise.resolve(a * b);
    }

    public static native int nativeMultiply(int a, int b);

}
AmirSalahY commented 1 year ago

OR THIS

package com.reactnativedatatrans;
import android.app.Activity;
import android.util.Log;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;

import ch.datatrans.payment.api.Transaction;
import ch.datatrans.payment.api.TransactionListener;
import ch.datatrans.payment.api.TransactionRegistry;
import ch.datatrans.payment.api.TransactionSuccess;
import ch.datatrans.payment.api.tokenization.PCIPCardInfo;
import ch.datatrans.payment.exception.TransactionException;
import ch.datatrans.payment.paymentmethods.Card;
import ch.datatrans.payment.paymentmethods.CardExpiryDate;
import ch.datatrans.payment.paymentmethods.PaymentMethodType;

public class DatatransModule extends ReactContextBaseJavaModule implements TransactionListener {
    DatatransModule(ReactApplicationContext context) {
        super(context);
    }
    private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
    private Promise mPromise;

    @NonNull
    @Override
    public String getName() {
        return "DatatransModule";
    }

    @ReactMethod
    public void logData(String name, String text) {
        Log.d("CalendarModule", "Create event called with name: " + name
                + " and location: " + text);
    }

    @ReactMethod
    public void initDatatrans( final Promise promise){
        Activity activity = getCurrentActivity();
        if (activity == null) {
            WritableMap map = Arguments.createMap();
            map.putString("message", "Activity doesn't exist");
            consumeCallback(E_ACTIVITY_DOES_NOT_EXIST, map);
            return;
        }

        try {
            Transaction transaction = new Transaction("8b26a173484a7fbfe82f7009d6e859f7033352e5efe77416");
            transaction.setListener(this); // this refers to Android's Activity
            transaction.getOptions().setTesting(true);
            transaction.getOptions().setUseCertificatePinning(true);
            TransactionRegistry.INSTANCE.startTransaction(activity, transaction);
        }catch (Exception e) {

            mPromise.reject(e);
        }
//        return transaction;
    }

    @Override
    public void onTransactionCancel() {
        Log.d("canceled","onTransaction Canceled");
    }

    @Override
    public void onTransactionError(@NonNull TransactionException e) {
        Log.d("error","onTransaction error");
    }

    @Override
    public void onTransactionSuccess(@NonNull TransactionSuccess transactionSuccess) {
        Log.d("success","onTransaction succeed ");
    }

    private void consumeCallback(String type, WritableMap map) {
        if (mPromise != null) {
            map.putString("type", type);
            map.putString("provider", "ct-datatrans");

            mPromise.resolve(map);
            mPromise = null;
        }
    }
}
luiscosta commented 1 year ago

Hello @AmirSalahY,

I will need the full stacktrace.

For the 8b26a173484a7fbfe82f7009d6e859f7033352e5efe77416 mobile token you are not setting the appCallbackScheme. When you use PAP you must define one.

Can you also tell me which is your appCallbackScheme? Note that it must comply with RFC 3986.

AmirSalahY commented 1 year ago

Thanks @luiscosta for your answer is there any reference u suggest to create one for android and one for ios?

luiscosta commented 1 year ago

Hello again @AmirSalahY,

If you are asking about the reference for the RFC 3986, you can follow the documentation at: https://datatracker.ietf.org/doc/html/rfc3986#autoid-18

bacherma commented 1 year ago

@AmirSalahY, can you please post the full stack trace? We suspect there may be a configuration error as @luiscosta mentioned. The fact that the dialog is shown means the SDK is invoked, so the React Native bridge is working. We need to see where that error is thrown.

AmirSalahY commented 1 year ago

which stack exactly do u need

bacherma commented 1 year ago

@AmirSalahY In the initial message you posted an image of an SDK error screen. This means that your app will receive a notification onTransactionError with an object of type TransactionException (link). Exceptions in Java have a concept called stack trace and something called exception chaining. We need the full stack trace of the exception chain. You can log it by adding the exception to your Log command. (The TransactionException is a Throwable.)

AmirSalahY commented 1 year ago

sorry for being late

2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : android.os.Handler.handleCallback(Handler.java:958)
2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : android.os.Handler.dispatchMessage(Handler.java:99)
2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : android.os.Looper.loopOnce(Looper.java:205)
2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : android.os.Looper.loop(Looper.java:294)
2023-06-25 11:14:05.964 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : android.app.ActivityThread.main(ActivityThread.java:8176)
2023-06-25 11:14:05.965 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : java.lang.reflect.Method.invoke(Native Method)
2023-06-25 11:14:05.965 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
2023-06-25 11:14:05.965 28942-28942 System.err              com.datatransexample                 W  ----------from onTransactionError : com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

i use these credentials

{
    "currency": "CHF",
    "refno": "Test-1234",
    "amount": 5,
    "paymentMethods": ["ECA","VIS","BON","REK"],
    "option": {
        "returnMobileToken": true
    }
}
luiscosta commented 1 year ago

Hello again @AmirSalahY,

Unfortunately, the stacktrace log you sent on your message is not the error we want to see.

Can you change your onTransactionError method to something like:

@Override
public void onTransactionError(@NonNull TransactionException e) {
    Log.e("error","onTransaction error", e);
}

This should give you the SDK error we want.

Thank you!

bacherma commented 1 year ago

@AmirSalahY any updates on this?

bacherma commented 1 year ago

Unclear status, likely misconfiguration. Closing the ticket.