Skip to content

Instantly share code, notes, and snippets.

@jamesl1001
Created November 25, 2021 12:10
Show Gist options
  • Save jamesl1001/5442f9108e56c1624421cbb09c6a1953 to your computer and use it in GitHub Desktop.
Save jamesl1001/5442f9108e56c1624421cbb09c6a1953 to your computer and use it in GitHub Desktop.

It seems Tipsi will not update the stripe module, as there is an official library from Stripe Itself.

I'm using stripe for Google pay and Apple Pay only. Even though I haven't used the Card payment, It is mandatory to update to 16.9.0/16.10.0 in order to prevent rejection.

One Good Way: Open Android Studio -> Go to Build.gradle of tipsi-stripe -> Update Stripe SDK to 16.9.0 (it will show you the tooltip dialog box to update to 16.9.0, as you hover over implementation 'com.stripe:stripe-android:10.4.6' line).

Click Sync now (in the header part of build.gradle)

Go to tipsi-stripe -> java ->com.gettipsi.stripe

The following Files will get affected after the update:

  • converters.java(under util folder)
  • GoogleApiPayFlowImpl.java
  • StripeModule.java

Open the respected files and change the old imports to new imports. You can fix most of the errors by hover over the error and do recommended fix.

Replace old code with below:

Converters.java

package com.gettipsi.stripe.util;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.google.android.gms.identity.intents.model.CountrySpecification;
import com.google.android.gms.identity.intents.model.UserAddress;
import com.google.android.gms.wallet.PaymentData;
import com.stripe.android.PaymentIntentResult;
import com.stripe.android.SetupIntentResult;
import com.stripe.android.model.Address;
import com.stripe.android.model.BankAccount;
import com.stripe.android.model.BankAccountTokenParams;
import com.stripe.android.model.Card;
import com.stripe.android.model.PaymentIntent;
import com.stripe.android.model.PaymentMethod;
import com.stripe.android.model.SetupIntent;
import com.stripe.android.model.Source;
import com.stripe.android.model.Source.CodeVerification;
import com.stripe.android.model.Source.Owner;
import com.stripe.android.model.Source.Receiver;
import com.stripe.android.model.Source.Redirect;
import com.stripe.android.model.Token;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

/**
 * Created by ngoriachev on 13/03/2018.
 */

public class Converters {

  public static WritableMap convertTokenToWritableMap(Token token) {
    WritableMap newToken = Arguments.createMap();

    if (token == null) return newToken;

    newToken.putString("tokenId", token.getId());
    newToken.putBoolean("livemode", token.getLivemode());
    newToken.putBoolean("used", token.getUsed());
    newToken.putDouble("created", token.getCreated().getTime());

    if (token.getCard() != null) {
      newToken.putMap("card", convertCardToWritableMap(token.getCard()));
    }
    if (token.getBankAccount() != null) {
      newToken.putMap("bankAccount", convertBankAccountToWritableMap(token.getBankAccount()));
    }

    return newToken;
  }

  public static WritableMap putExtraToTokenMap(final WritableMap tokenMap, UserAddress billingAddress, UserAddress shippingAddress, String emailAddress) {
    ArgCheck.nonNull(tokenMap);

    WritableMap extra = Arguments.createMap();

    //add email address to billing and shipping contact as per apple
    WritableMap billingContactMap = convertAddressToWritableMap(billingAddress);
    WritableMap shippingContactMap = convertAddressToWritableMap(shippingAddress);

    billingContactMap.putString("emailAddress", emailAddress);
    shippingContactMap.putString("emailAddress", emailAddress);


    extra.putMap("billingContact", billingContactMap);
    extra.putMap("shippingContact", shippingContactMap);

    tokenMap.putMap("extra", extra);

    return tokenMap;
  }

  private static WritableMap convertCardToWritableMap(final Card card) {
    WritableMap result = Arguments.createMap();

    if (card == null) return result;

    result.putString("cardId", card.getId());
    result.putString("number", card.getNumber());
    result.putString("cvc", card.getCvc() );
    result.putInt("expMonth", card.getExpMonth() );
    result.putInt("expYear", card.getExpYear() );
    result.putString("name", card.getName() );
    result.putString("addressLine1", card.getAddressLine1() );
    result.putString("addressLine2", card.getAddressLine2() );
    result.putString("addressCity", card.getAddressCity() );
    result.putString("addressState", card.getAddressState() );
    result.putString("addressZip", card.getAddressZip() );
    result.putString("addressCountry", card.getAddressCountry() );
    result.putString("last4", card.getLast4() );
    result.putString("fingerprint", card.getFingerprint() );
    result.putString("country", card.getCountry() );
    result.putString("currency", card.getCurrency() );

    return result;
  }

  public static WritableMap convertBankAccountToWritableMap(BankAccount account) {
    WritableMap result = Arguments.createMap();

    if (account == null) return result;

    result.putString("routingNumber", account.getRoutingNumber());
    result.putString("countryCode", account.getCountryCode());
    result.putString("currency", account.getCurrency());
    result.putString("accountHolderName", account.getAccountHolderName());
    result.putString("fingerprint", account.getFingerprint());
    result.putString("bankName", account.getBankName());
    result.putString("last4", account.getLast4());

    return result;
  }

  public static String getValue(final ReadableMap map, final String key, final String def) {
    if (map.hasKey(key)) {
      return map.getString(key);
    } else {
      // If map don't have some key - we must pass to constructor default value.
      return def;
    }
  }

  public static Boolean getValue(final ReadableMap map, final String key, final Boolean def) {
    if (map.hasKey(key)) {
      return map.getBoolean(key);
    } else {
      // If map don't have some key - we must pass to constructor default value.
      return def;
    }
  }

  public static ReadableArray getValue(final ReadableMap map, final String key, final ReadableArray def) {
    if (map.hasKey(key)) {
      return map.getArray(key);
    } else {
      // If map don't have some key - we must pass to constructor default value.
      return def;
    }
  }

  public static String getValue(final ReadableMap map, final String key) {
    return getValue(map, key, (String) null);
  }

  public static Collection<String> getAllowedShippingCountryCodes(final ReadableMap map) {
    ArrayList<String> allowedCountryCodesForShipping = new ArrayList<>();
    ReadableArray countries = getValue(map, "shipping_countries", (ReadableArray) null);

    if (countries != null){
      for (int i = 0; i < countries.size(); i++) {
        String code = countries.getString(i);
        allowedCountryCodesForShipping.add(code);
      }
    }

    return allowedCountryCodesForShipping;
  }

  public static ArrayList<CountrySpecification> getAllowedShippingCountries(final ReadableMap map) {
    ArrayList<CountrySpecification> allowedCountriesForShipping = new ArrayList<>();
    ReadableArray countries = getValue(map, "shipping_countries", (ReadableArray) null);

    if (countries != null){
      for (int i = 0; i < countries.size(); i++) {
        String code = countries.getString(i);
        allowedCountriesForShipping.add(new CountrySpecification(code));
      }
    }

    return allowedCountriesForShipping;
  }

  public static Card createCard(final ReadableMap cardData) {
    return new Card.Builder(
        cardData.getString("number"),
        cardData.getInt("expMonth"),
        cardData.getInt("expYear"),
        getValue(cardData, "cvc"))
      .name(getValue(cardData, "name"))
      .addressLine1(getValue(cardData, "addressLine1"))
      .addressLine2(getValue(cardData, "addressLine2"))
      .addressCity(getValue(cardData, "addressCity"))
      .addressState(getValue(cardData, "addressState"))
      .addressZip(getValue(cardData, "addressZip"))
      .addressCountry(getValue(cardData, "addressCountry"))
      .last4(getValue(cardData, "last4"))
      .fingerprint(getValue(cardData, "fingerprint"))
      .country(getValue(cardData, "country"))
      .currency(getValue(cardData, "currency"))
      .id(getValue(cardData, "id"))
      .build();
  }



  @NonNull
  public static WritableMap convertSourceToWritableMap(@Nullable Source source) {
    WritableMap newSource = Arguments.createMap();

    if (source == null) {
      return newSource;
    }

    newSource.putString("sourceId", source.getId());
    newSource.putInt("amount", source.getAmount().intValue());
    newSource.putInt("created", source.getCreated().intValue());
    newSource.putString("currency", source.getCurrency());
    newSource.putString("flow", String.valueOf(source.getFlow()));
    newSource.putBoolean("livemode", source.isLiveMode());
    newSource.putMap("metadata", stringMapToWritableMap(source.getMetaData()));
    newSource.putMap("owner", convertOwnerToWritableMap(source.getOwner()));
    newSource.putMap("receiver", convertReceiverToWritableMap(source.getReceiver()));
    newSource.putMap("redirect", convertRedirectToWritableMap(source.getRedirect()));
    newSource.putMap("sourceTypeData", mapToWritableMap(source.getSourceTypeData()));
    newSource.putString("status", String.valueOf(source.getStatus()));
    newSource.putString("type", source.getType());
    newSource.putString("typeRaw", source.getTypeRaw());
    newSource.putString("usage", String.valueOf(source.getUsage()));

    return newSource;
  }

  @NonNull
  public static WritableMap convertPaymentIntentResultToWritableMap(@Nullable PaymentIntentResult paymentIntentResult) {
    WritableMap wm = Arguments.createMap();

    if (paymentIntentResult == null) {
      wm.putString("status", "unknown");
      return wm;
    }

    PaymentIntent intent = paymentIntentResult.getIntent();
    wm.putString("status", intent.getStatus().toString());
    wm.putString("paymentIntentId", intent.getId());

//    String paymentMethodId = intent.getPaymentMethodId();
//    if (paymentMethodId != null) {
//      wm.putString("paymentMethodId", paymentMethodId);
//    }
    return wm;
  }


  @NonNull
  public static WritableMap convertSetupIntentResultToWritableMap(@Nullable SetupIntentResult setupIntentResult) {
    WritableMap wm = Arguments.createMap();

    if (setupIntentResult == null) {
      wm.putString("status", "unknown");
      return wm;
    }

    SetupIntent intent = setupIntentResult.getIntent();
    wm.putString("status", intent.getStatus().toString());
    wm.putString("setupIntentId", intent.getId());

    String paymentMethodId = intent.getPaymentMethodId();
    if (paymentMethodId != null) {
      wm.putString("paymentMethodId", paymentMethodId);
    }
    return wm;
  }

  @NonNull
  public static WritableMap convertPaymentMethodToWritableMap(@Nullable PaymentMethod paymentMethod) {
    WritableMap wm = Arguments.createMap();

    if (paymentMethod == null) {
      return wm;
    }

    wm.putString("id", paymentMethod.id);
    wm.putInt("created", paymentMethod.created.intValue());
    wm.putBoolean("livemode", paymentMethod.liveMode);
    wm.putString("type", String.valueOf(paymentMethod.type));
    wm.putMap("billingDetails", convertBillingDetailsToWritableMap(paymentMethod.billingDetails));
    wm.putMap("card", convertPaymentMethodCardToWritableMap(paymentMethod.card));
    wm.putString("customerId", paymentMethod.customerId);

    // TODO support metadata
    return wm;
  }

  @NonNull
  public static WritableMap convertPaymentMethodCardToWritableMap(@Nullable final PaymentMethod.Card card) {
    WritableMap wm = Arguments.createMap();

    if (card == null) {
      return wm;
    }

    // Omitted (can be introduced later): card.checks, card.threeDSecureUsage, card.wallet

    wm.putString("brand", String.valueOf(card.brand));
    wm.putString("country", card.country);
    wm.putInt("expMonth", card.expiryMonth);
    wm.putInt("expYear", card.expiryYear);
    wm.putString("funding", card.funding);
    wm.putString("last4", card.last4);
    return wm;
  }

  @NonNull
  public static WritableMap convertBillingDetailsToWritableMap(@Nullable final PaymentMethod.BillingDetails billingDetails) {
    WritableMap wm = Arguments.createMap();

    if (billingDetails == null) {
      return wm;
    }

    wm.putMap("address", convertAddressToWritableMap(billingDetails.address));
    wm.putString("email", billingDetails.email);
    wm.putString("name", billingDetails.name);
    wm.putString("phone", billingDetails.phone);
    return wm;
  }


  @NonNull
  public static WritableMap stringMapToWritableMap(@Nullable Map<String, String> map) {
    WritableMap writableMap = Arguments.createMap();

    if (map == null) {
      return writableMap;
    }

    for (Map.Entry<String, String> entry : map.entrySet()) {
      writableMap.putString(entry.getKey(), entry.getValue());
    }

    return writableMap;
  }

  @NonNull
  public static WritableMap convertOwnerToWritableMap(@Nullable final Owner owner) {
    WritableMap map = Arguments.createMap();

    if (owner == null) {
      return map;
    }

    map.putMap("address", convertAddressToWritableMap(owner.getAddress()));
    map.putString("email", owner.getEmail());
    map.putString("name", owner.getName());
    map.putString("phone", owner.getPhone());
    map.putString("verifiedEmail", owner.getVerifiedEmail());
    map.putString("verifiedPhone", owner.getVerifiedPhone());
    map.putString("verifiedName", owner.getVerifiedName());
    map.putMap("verifiedAddress", convertAddressToWritableMap(owner.getVerifiedAddress()));

    return map;
  }

  @NonNull
  public static WritableMap convertAddressToWritableMap(@Nullable final Address address) {
    WritableMap map = Arguments.createMap();

    if (address == null) {
      return map;
    }

    map.putString("city", address.getCity());
    map.putString("country", address.getCountry());
    map.putString("line1", address.getLine1());
    map.putString("line2", address.getLine2());
    map.putString("postalCode", address.getPostalCode());
    map.putString("state", address.getState());

    return map;
  }

  @NonNull
  public static WritableMap convertReceiverToWritableMap(@Nullable final Receiver receiver) {
    WritableMap map = Arguments.createMap();

    if (receiver == null) {
      return map;
    }

    map.putInt("amountCharged", (int) receiver.getAmountCharged());
    map.putInt("amountReceived", (int) receiver.getAmountReceived());
    map.putInt("amountReturned", (int) receiver.getAmountReturned());
    map.putString("address", receiver.getAddress());

    return map;
  }

  @NonNull
  public static WritableMap convertRedirectToWritableMap(@Nullable Redirect redirect) {
    WritableMap map = Arguments.createMap();

    if (redirect == null) {
      return map;
    }

    map.putString("returnUrl", redirect.getReturnUrl());
    map.putString("status", String.valueOf(redirect.getStatus()));
    map.putString("url", redirect.getUrl());

    return map;
  }

  @NonNull
  public static WritableMap convertCodeVerificationToWritableMap(@Nullable CodeVerification codeVerification) {
    WritableMap map = Arguments.createMap();

    if (codeVerification == null) {
      return map;
    }

    map.putInt("attemptsRemaining", codeVerification.getAttemptsRemaining());
    map.putString("status", String.valueOf(codeVerification.getStatus()));

    return map;
  }

  @NonNull
  public static WritableMap mapToWritableMap(@Nullable Map<String, Object> map){
    WritableMap writableMap = Arguments.createMap();

    if (map == null) {
      return writableMap;
    }

    for (String key: map.keySet()) {
      pushRightTypeToMap(writableMap, key, map.get(key));
    }

    return writableMap;
  }

  public static void pushRightTypeToMap(@NonNull WritableMap map, @NonNull String key, @NonNull Object object) {
    Class argumentClass = object.getClass();
    if (argumentClass == Boolean.class) {
      map.putBoolean(key, (Boolean) object);
    } else if (argumentClass == Integer.class) {
      map.putDouble(key, ((Integer)object).doubleValue());
    } else if (argumentClass == Double.class) {
      map.putDouble(key, (Double) object);
    } else if (argumentClass == Float.class) {
      map.putDouble(key, ((Float)object).doubleValue());
    } else if (argumentClass == String.class) {
      map.putString(key, object.toString());
    } else if (argumentClass == WritableNativeMap.class) {
      map.putMap(key, (WritableNativeMap)object);
    } else if (argumentClass == WritableNativeArray.class) {
      map.putArray(key, (WritableNativeArray) object);
    } else {

    }
  }

  public static WritableMap convertAddressToWritableMap(final UserAddress address){
    WritableMap result = Arguments.createMap();

    if (address == null) return result;

    putIfNotEmpty(result, "address1", address.getAddress1());
    putIfNotEmpty(result, "address2", address.getAddress2());
    putIfNotEmpty(result, "address3", address.getAddress3());
    putIfNotEmpty(result, "address4", address.getAddress4());
    putIfNotEmpty(result, "address5", address.getAddress5());
    putIfNotEmpty(result, "administrativeArea", address.getAdministrativeArea());
    putIfNotEmpty(result, "companyName", address.getCompanyName());
    putIfNotEmpty(result, "countryCode", address.getCountryCode());
    putIfNotEmpty(result, "locality", address.getLocality());
    putIfNotEmpty(result, "name", address.getName());
    putIfNotEmpty(result, "phoneNumber", address.getPhoneNumber());
    putIfNotEmpty(result, "postalCode", address.getPostalCode());
    putIfNotEmpty(result, "sortingCode", address.getSortingCode());

    return result;
  }


  public static String getStringOrNull(@NonNull ReadableMap map, @NonNull String key) {
    return map.hasKey(key) ? map.getString(key) : null;
  }

  public static ReadableMap getMapOrNull(@NonNull ReadableMap map, @NonNull String key) {
    return map.hasKey(key) ? map.getMap(key) : null;
  }

  public static boolean getBooleanOrNull(@NonNull ReadableMap map, @NonNull String key, boolean defaultVal) {
    return map.hasKey(key) ? map.getBoolean(key) : defaultVal;
  }

  public static void putIfNotEmpty(final WritableMap map, final String key, final String value) {
    if (!TextUtils.isEmpty(value)) {
      map.putString(key, value);
    }
  }

  public static UserAddress getBillingAddress(PaymentData paymentData) {
    if (paymentData != null && paymentData.getCardInfo() != null) {
      return paymentData.getCardInfo().getBillingAddress();
    }

    return null;
  }

}

Open GoogleApiPayFlowImpl.java

package com.gettipsi.stripe;

import android.app.Activity;
import android.content.Intent;
import androidx.annotation.NonNull;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableMap;
import com.gettipsi.stripe.util.ArgCheck;
import com.gettipsi.stripe.util.Converters;
import com.gettipsi.stripe.util.Fun0;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.wallet.AutoResolveHelper;
import com.google.android.gms.wallet.CardRequirements;
import com.google.android.gms.wallet.IsReadyToPayRequest;
import com.google.android.gms.wallet.PaymentData;
import com.google.android.gms.wallet.PaymentDataRequest;
import com.google.android.gms.wallet.PaymentMethodTokenizationParameters;
import com.google.android.gms.wallet.PaymentsClient;
import com.google.android.gms.wallet.ShippingAddressRequirements;
import com.google.android.gms.wallet.TransactionInfo;
import com.google.android.gms.wallet.Wallet;
import com.google.android.gms.wallet.WalletConstants;
import com.stripe.android.BuildConfig;
import com.stripe.android.Stripe;
import com.stripe.android.model.GooglePayResult;
import com.stripe.android.model.Token;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.Arrays;
import java.util.Collection;

import static com.gettipsi.stripe.Errors.toErrorCode;
import static com.gettipsi.stripe.util.Converters.convertTokenToWritableMap;
import static com.gettipsi.stripe.util.Converters.getAllowedShippingCountryCodes;
import static com.gettipsi.stripe.util.Converters.getBillingAddress;
import static com.gettipsi.stripe.util.Converters.putExtraToTokenMap;
import static com.gettipsi.stripe.util.PayParams.CURRENCY_CODE;
import static com.gettipsi.stripe.util.PayParams.BILLING_ADDRESS_REQUIRED;
import static com.gettipsi.stripe.util.PayParams.SHIPPING_ADDRESS_REQUIRED;
import static com.gettipsi.stripe.util.PayParams.PHONE_NUMBER_REQUIRED;
import static com.gettipsi.stripe.util.PayParams.EMAIL_REQUIRED;
import static com.gettipsi.stripe.util.PayParams.TOTAL_PRICE;

/**
 * Created by ngoriachev on 13/03/2018.
 * see https://developers.google.com/pay/api/tutorial
 */
public final class GoogleApiPayFlowImpl extends PayFlow {

  private static final String TAG = GoogleApiPayFlowImpl.class.getSimpleName();
  private static final int LOAD_PAYMENT_DATA_REQUEST_CODE = 65534;

  private PaymentsClient mPaymentsClient;
  private Promise payPromise;

  public GoogleApiPayFlowImpl(@NonNull Fun0<Activity> activityProvider) {
    super(activityProvider);
  }

  private PaymentsClient createPaymentsClient(@NonNull Activity activity) {
    return Wallet.getPaymentsClient(
      activity,
      new Wallet.WalletOptions.Builder().setEnvironment(getEnvironment()).build());
  }

  private void isReadyToPay(@NonNull Activity activity, boolean isExistingPaymentMethodRequired, @NonNull final Promise promise) {
    ArgCheck.nonNull(activity);
    ArgCheck.nonNull(promise);

    IsReadyToPayRequest request =
      IsReadyToPayRequest.newBuilder()
        .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
        .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
        .setExistingPaymentMethodRequired(isExistingPaymentMethodRequired)
        .build();
    mPaymentsClient = createPaymentsClient(activity);
    Task<Boolean> task = mPaymentsClient.isReadyToPay(request);
    task.addOnCompleteListener(
      new OnCompleteListener<Boolean>() {
        public void onComplete(Task<Boolean> task) {
          try {
            boolean result = task.getResult(ApiException.class);
            promise.resolve(result);
          } catch (ApiException exception) {
            promise.reject(toErrorCode(exception), exception.getMessage());
          }
        }
      });
  }

  private PaymentMethodTokenizationParameters createPaymentMethodTokenizationParameters() {
    return PaymentMethodTokenizationParameters.newBuilder()
      .setPaymentMethodTokenizationType(WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY)
      .addParameter("gateway", "stripe")
      .addParameter("stripe:publishableKey", getPublishableKey())
      .addParameter("stripe:version", Stripe.VERSION_NAME)
      .build();
  }

  private PaymentDataRequest createPaymentDataRequest(ReadableMap payParams) {
    final String estimatedTotalPrice = payParams.getString(TOTAL_PRICE);
    final String currencyCode = payParams.getString(CURRENCY_CODE);
    final boolean billingAddressRequired = Converters.getValue(payParams, BILLING_ADDRESS_REQUIRED, false);
    final boolean shippingAddressRequired = Converters.getValue(payParams, SHIPPING_ADDRESS_REQUIRED, false);
    final boolean phoneNumberRequired = Converters.getValue(payParams, PHONE_NUMBER_REQUIRED, false);
    final boolean emailRequired = Converters.getValue(payParams, EMAIL_REQUIRED, false);
    final Collection<String> allowedCountryCodes = getAllowedShippingCountryCodes(payParams);

    return createPaymentDataRequest(
      estimatedTotalPrice,
      currencyCode,
      billingAddressRequired,
      shippingAddressRequired,
      phoneNumberRequired,
      emailRequired,
      allowedCountryCodes
    );
  }

  private PaymentDataRequest createPaymentDataRequest(@NonNull final String totalPrice,
                                                      @NonNull final String currencyCode,
                                                      final boolean billingAddressRequired,
                                                      final boolean shippingAddressRequired,
                                                      final boolean phoneNumberRequired,
                                                      final boolean emailRequired,
                                                      @NonNull final Collection<String> countryCodes
  ) {

    ArgCheck.isDouble(totalPrice);
    ArgCheck.notEmptyString(currencyCode);

    PaymentDataRequest.Builder builder = PaymentDataRequest.newBuilder();
    builder.setTransactionInfo(
      TransactionInfo.newBuilder()
        .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_ESTIMATED)
        .setTotalPrice(totalPrice)
        .setCurrencyCode(currencyCode)
        .build());

    builder
      .setCardRequirements(
        CardRequirements.newBuilder()
          .addAllowedCardNetworks(
            Arrays.asList(
              WalletConstants.CARD_NETWORK_AMEX,
              WalletConstants.CARD_NETWORK_DISCOVER,
              WalletConstants.CARD_NETWORK_VISA,
              WalletConstants.CARD_NETWORK_MASTERCARD))
          .setBillingAddressRequired(billingAddressRequired)
          .build())
      .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
      .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
      .setEmailRequired(emailRequired)
      .setShippingAddressRequired(shippingAddressRequired)
      .setPhoneNumberRequired(phoneNumberRequired);

    if (countryCodes.size() > 0) {
      builder.setShippingAddressRequirements(
        ShippingAddressRequirements.newBuilder()
          .addAllowedCountryCodes(countryCodes)
          .build());
    }

    builder.setPaymentMethodTokenizationParameters(createPaymentMethodTokenizationParameters());
    return builder.build();
  }

  private void startPaymentRequest(@NonNull Activity activity, @NonNull PaymentDataRequest request) {
    ArgCheck.nonNull(activity);
    ArgCheck.nonNull(request);

    mPaymentsClient = createPaymentsClient(activity);

    AutoResolveHelper.resolveTask(
      mPaymentsClient.loadPaymentData(request),
      activity,
      LOAD_PAYMENT_DATA_REQUEST_CODE);
  }

  @Override
  public void paymentRequestWithAndroidPay(@NonNull ReadableMap payParams, @NonNull Promise promise) {
    ArgCheck.nonNull(payParams);
    ArgCheck.nonNull(promise);

    Activity activity = activityProvider.call();
    if (activity == null) {
      promise.reject(
        getErrorCode("activityUnavailable"),
        getErrorDescription("activityUnavailable")
      );
      return;
    }

    this.payPromise = promise;
    startPaymentRequest(activity, createPaymentDataRequest(payParams));
  }

  @Override
  public void deviceSupportsAndroidPay(boolean isExistingPaymentMethodRequired, @NonNull Promise promise) {
    Activity activity = activityProvider.call();
    if (activity == null) {
      promise.reject(
        getErrorCode("activityUnavailable"),
        getErrorDescription("activityUnavailable")
      );
      return;
    }

    if (!isPlayServicesAvailable(activity)) {
      promise.reject(
        getErrorCode("playServicesUnavailable"),
        getErrorDescription("playServicesUnavailable")
      );
      return;
    }

    isReadyToPay(activity, isExistingPaymentMethodRequired, promise);
  }

  public boolean onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
    if (payPromise == null) {
      return false;
    }

    switch (requestCode) {
      case LOAD_PAYMENT_DATA_REQUEST_CODE:
        switch (resultCode) {
          case Activity.RESULT_OK:
            PaymentData paymentData = PaymentData.getFromIntent(data);
            ArgCheck.nonNull(paymentData);
            String tokenJson = paymentData.getPaymentMethodToken().getToken();
            JSONObject obj = null;
            try {
              obj = new JSONObject(tokenJson);
            } catch (JSONException e) {
              e.printStackTrace();
            }
            Token token = Token.fromJson(obj);
            if (token == null) {
              payPromise.reject(
                getErrorCode("parseResponse"),
                getErrorDescription("parseResponse")
              );
            } else {
              payPromise.resolve(putExtraToTokenMap(
                convertTokenToWritableMap(token),
                getBillingAddress(paymentData),
                paymentData.getShippingAddress(),
                paymentData.getEmail()));
            }
            break;
          case Activity.RESULT_CANCELED:
            payPromise.reject(
              getErrorCode("purchaseCancelled"),
              getErrorDescription("purchaseCancelled")
            );
            break;
          case AutoResolveHelper.RESULT_ERROR:
            Status status = AutoResolveHelper.getStatusFromIntent(data);
            // Log the status for debugging.
            // Generally, there is no need to show an error to
            // the user as the Google Pay API will do that.
            payPromise.reject(
              getErrorCode("stripe"),
              status.getStatusMessage()
            );
            break;

          default:
            // Do nothing.
        }
        payPromise = null;
        return true;
    }

    return false;
  }

}

StripeModule.java

package com.gettipsi.stripe;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.BaseActivityEventListener;
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.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.gettipsi.stripe.dialog.AddCardDialogFragment;
import com.gettipsi.stripe.util.ArgCheck;
import com.gettipsi.stripe.util.Converters;
import com.gettipsi.stripe.util.Fun0;
import com.google.android.gms.wallet.WalletConstants;
import com.stripe.android.ApiResultCallback;
import com.stripe.android.AppInfo;
import com.stripe.android.PaymentIntentResult;
import com.stripe.android.SetupIntentResult;
import com.stripe.android.ApiResultCallback;
import com.stripe.android.Stripe;
import com.stripe.android.model.Address;
import com.stripe.android.model.ConfirmPaymentIntentParams;
import com.stripe.android.model.ConfirmSetupIntentParams;
import com.stripe.android.model.PaymentMethod;
import com.stripe.android.model.PaymentMethodCreateParams;
import com.stripe.android.model.Source;
import com.stripe.android.model.Source.Status;
import com.stripe.android.model.SourceParams;
import com.stripe.android.model.StripeIntent;
import com.stripe.android.model.Token;

import java.util.HashMap;
import java.util.Map;

import static com.gettipsi.stripe.Errors.AUTHENTICATION_FAILED;
import static com.gettipsi.stripe.Errors.CANCELLED;
import static com.gettipsi.stripe.Errors.FAILED;
import static com.gettipsi.stripe.Errors.UNEXPECTED;
import static com.gettipsi.stripe.Errors.getDescription;
import static com.gettipsi.stripe.Errors.getErrorCode;
import static com.gettipsi.stripe.Errors.toErrorCode;
import static com.gettipsi.stripe.util.Converters.convertPaymentIntentResultToWritableMap;
import static com.gettipsi.stripe.util.Converters.convertPaymentMethodToWritableMap;
import static com.gettipsi.stripe.util.Converters.convertSetupIntentResultToWritableMap;
import static com.gettipsi.stripe.util.Converters.convertSourceToWritableMap;
import static com.gettipsi.stripe.util.Converters.convertTokenToWritableMap;
import static com.gettipsi.stripe.util.Converters.createCard;
import static com.gettipsi.stripe.util.Converters.getBooleanOrNull;
import static com.gettipsi.stripe.util.Converters.getMapOrNull;
import static com.gettipsi.stripe.util.Converters.getStringOrNull;
import static com.gettipsi.stripe.util.InitializationOptions.ANDROID_PAY_MODE_KEY;
import static com.gettipsi.stripe.util.InitializationOptions.ANDROID_PAY_MODE_PRODUCTION;
import static com.gettipsi.stripe.util.InitializationOptions.ANDROID_PAY_MODE_TEST;
import static com.gettipsi.stripe.util.InitializationOptions.PUBLISHABLE_KEY;
import static com.stripe.android.model.StripeIntent.Status.Canceled;
import static com.stripe.android.model.StripeIntent.Status.RequiresAction;
import static com.stripe.android.model.StripeIntent.Status.RequiresCapture;
import static com.stripe.android.model.StripeIntent.Status.RequiresConfirmation;
import static com.stripe.android.model.StripeIntent.Status.Succeeded;

public class StripeModule extends ReactContextBaseJavaModule {

  private static final String MODULE_NAME = StripeModule.class.getSimpleName();

  // If you change these, make sure to also change:
  //  ios/TPSStripe/TPSStripeManager
  // Relevant Docs:
  // - https://stripe.dev/stripe-ios/docs/Classes/STPAppInfo.html https://stripe.dev/stripe-android/com/stripe/android/AppInfo.html
  // - https://stripe.com/docs/building-plugins#setappinfo
  private static final String APP_INFO_NAME    = "tipsi-stripe";
  private static final String APP_INFO_URL     = "https://github.com/tipsi/tipsi-stripe";
  private static final String APP_INFO_VERSION = "8.x";
  public static final String CLIENT_SECRET = "clientSecret";

  private static StripeModule sInstance = null;

  public static StripeModule getInstance() {
    return sInstance;
  }

  public Stripe getStripe() {
    return mStripe;
  }

  @Nullable
  private Promise mCreateSourcePromise;

  @Nullable
  private Source mCreatedSource;

  private String mPublicKey;
  private Stripe mStripe;
  private PayFlow mPayFlow;
  private ReadableMap mErrorCodes;

  private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
      boolean handled = getPayFlow().onActivityResult(activity, requestCode, resultCode, data);
      if (!handled) {
        super.onActivityResult(activity, requestCode, resultCode, data);
      }
    }
  };


  public StripeModule(ReactApplicationContext reactContext) {
    super(reactContext);

    // Add the listener for `onActivityResult`
    reactContext.addActivityEventListener(mActivityEventListener);

    sInstance = this;
  }

  @Override
  public String getName() {
    return MODULE_NAME;
  }

  @ReactMethod
  public void init(@NonNull ReadableMap options, @NonNull ReadableMap errorCodes) {
    ArgCheck.nonNull(options);

    String newPubKey = Converters.getStringOrNull(options, PUBLISHABLE_KEY);
    String newAndroidPayMode = Converters.getStringOrNull(options, ANDROID_PAY_MODE_KEY);

    if (newPubKey != null && !TextUtils.equals(newPubKey, mPublicKey)) {
      ArgCheck.notEmptyString(newPubKey);

      mPublicKey = newPubKey;
      Stripe.setAppInfo(AppInfo.create(APP_INFO_NAME, APP_INFO_VERSION, APP_INFO_URL));
      mStripe = new Stripe(getReactApplicationContext(), mPublicKey);
      getPayFlow().setPublishableKey(mPublicKey);
    }

    if (newAndroidPayMode != null) {
      ArgCheck.isTrue(ANDROID_PAY_MODE_TEST.equals(newAndroidPayMode) || ANDROID_PAY_MODE_PRODUCTION.equals(newAndroidPayMode));

      getPayFlow().setEnvironment(androidPayModeToEnvironment(newAndroidPayMode));
    }

    if (mErrorCodes == null) {
      mErrorCodes = errorCodes;
      getPayFlow().setErrorCodes(errorCodes);
    }
  }

  private PayFlow getPayFlow() {
    if (mPayFlow == null) {
      mPayFlow = PayFlow.create(
        new Fun0<Activity>() { public Activity call() {
          return getCurrentActivity();
        }}
      );
    }

    return mPayFlow;
  }

  private static int androidPayModeToEnvironment(@NonNull String androidPayMode) {
    ArgCheck.notEmptyString(androidPayMode);
    return ANDROID_PAY_MODE_TEST.equals(androidPayMode.toLowerCase()) ? WalletConstants.ENVIRONMENT_TEST : WalletConstants.ENVIRONMENT_PRODUCTION;
  }

  @ReactMethod
  public void deviceSupportsAndroidPay(final Promise promise) {
    getPayFlow().deviceSupportsAndroidPay(false, promise);
  }

  @ReactMethod
  public void canMakeAndroidPayPayments(final Promise promise) {
    getPayFlow().deviceSupportsAndroidPay(true, promise);
  }

  @ReactMethod
  public void setStripeAccount(final String stripeAccount) {
    ArgCheck.notEmptyString(mPublicKey);
    if (stripeAccount == null) {
      mStripe = new Stripe(getReactApplicationContext(), mPublicKey);
    } else {
      mStripe = new Stripe(getReactApplicationContext(), mPublicKey, stripeAccount);
    }
  }

  @ReactMethod
  public void createTokenWithCard(final ReadableMap cardData, final Promise promise) {
    try {
      ArgCheck.nonNull(mStripe);
      ArgCheck.notEmptyString(mPublicKey);

      mStripe.createCardToken(
        createCard(cardData),
        mPublicKey,
        new ApiResultCallback<Token>() {
          public void onSuccess(Token token) {
            promise.resolve(convertTokenToWritableMap(token));
          }
          public void onError(Exception error) {
            error.printStackTrace();
            promise.reject(toErrorCode(error), error.getMessage());
          }
        });
    } catch (Exception e) {
      promise.reject(toErrorCode(e), e.getMessage());
    }
  }

  @ReactMethod
  public void createTokenWithBankAccount(final ReadableMap accountData, final Promise promise) {
    try {
      ArgCheck.nonNull(mStripe);
      ArgCheck.notEmptyString(mPublicKey);
    } catch (Exception e) {
      promise.reject(toErrorCode(e), e.getMessage());
    }
  }

  @ReactMethod
  public void paymentRequestWithCardForm(ReadableMap params, final Promise promise) {
    Activity currentActivity = getCurrentActivity();
    try {
      ArgCheck.nonNull(currentActivity);
      ArgCheck.notEmptyString(mPublicKey);

      final AddCardDialogFragment cardDialog = AddCardDialogFragment.newInstance(
        getErrorCode(mErrorCodes, "cancelled"),
        getDescription(mErrorCodes, "cancelled")
      );
      cardDialog.setPromise(promise);
      cardDialog.show(currentActivity.getFragmentManager(), "AddNewCard");
    } catch (Exception e) {
      promise.reject(toErrorCode(e), e.getMessage());
    }
  }

  @ReactMethod
  public void paymentRequestWithAndroidPay(final ReadableMap payParams, final Promise promise) {
    getPayFlow().paymentRequestWithAndroidPay(payParams, promise);
  }

  private void attachPaymentResultActivityListener(final Promise promise) {
    ActivityEventListener ael = new BaseActivityEventListener() {

      @Override
      public void onActivityResult(Activity a, int requestCode, int resultCode, Intent data) {
        final ActivityEventListener ael = this;

        mStripe.onPaymentResult(requestCode, data, new ApiResultCallback<PaymentIntentResult>() {
          @Override
          public void onSuccess(@NonNull PaymentIntentResult result) {
            getReactApplicationContext().removeActivityEventListener(ael);

            StripeIntent.Status resultingStatus = result.getIntent().getStatus();

            if (Succeeded.equals(resultingStatus) ||
                RequiresCapture.equals(resultingStatus) ||
                RequiresConfirmation.equals(resultingStatus)) {
              promise.resolve(convertPaymentIntentResultToWritableMap(result));
            } else {
              if (Canceled.equals(resultingStatus) ||
                  RequiresAction.equals(resultingStatus)
              ) {
                promise.reject(CANCELLED, CANCELLED);      // TODO - normalize the message
              } else {
                promise.reject(FAILED, FAILED);
              }
            }
          }

          @Override
          public void onError(@NonNull Exception e) {
            getReactApplicationContext().removeActivityEventListener(ael);
            e.printStackTrace();
            promise.reject(toErrorCode(e), e.getMessage());
          }
        });
      }

      @Override
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
        onActivityResult(null, requestCode, resultCode, data);
      }
    };
    getReactApplicationContext().addActivityEventListener(ael);
  }

  private void attachSetupResultActivityListener(final Promise promise) {
    ActivityEventListener ael = new BaseActivityEventListener() {
      @Override
      public void onActivityResult(Activity a, int requestCode, int resultCode, Intent data) {
        final ActivityEventListener ael = this;

        mStripe.onSetupResult(requestCode, data, new ApiResultCallback<SetupIntentResult>() {
          @Override
          public void onSuccess(@NonNull SetupIntentResult result) {
            getReactApplicationContext().removeActivityEventListener(ael);

            try {
              switch (result.getIntent().getStatus()) {
                case Canceled:
                  // The Setup Intent was canceled, so reject the promise with a predefined code.
                  promise.reject(CANCELLED, "The SetupIntent was canceled by the user.");
                  break;
                case RequiresAction:
                case RequiresPaymentMethod:
                  promise.reject(AUTHENTICATION_FAILED, "The user failed authentication.");
                  break;
                case Succeeded:
                  promise.resolve(convertSetupIntentResultToWritableMap(result));
                  break;
                case RequiresCapture:
                case RequiresConfirmation:
                default:
                  promise.reject(UNEXPECTED, "Unexpected state");
              }
            } catch (Exception e) {
              promise.reject(UNEXPECTED, "Unexpected error");
            }
          }

          @Override
          public void onError(@NonNull Exception e) {
            getReactApplicationContext().removeActivityEventListener(ael);
            e.printStackTrace();
            promise.reject(toErrorCode(e), e.getMessage());
          }
        });
      }

      @Override
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
        onActivityResult(null, requestCode, resultCode, data);
      }
    };
    getReactApplicationContext().addActivityEventListener(ael);
  }

  @ReactMethod
  public void confirmPaymentIntent(final ReadableMap options, final Promise promise) {
    attachPaymentResultActivityListener(promise);

    Activity activity = getCurrentActivity();
    if (activity != null) {
      mStripe.confirmPayment(activity, extractConfirmPaymentIntentParams(options));
    }
  }

  @ReactMethod
  public void authenticatePaymentIntent(final ReadableMap options, final Promise promise) {
    attachPaymentResultActivityListener(promise);

    String clientSecret = options.getString(CLIENT_SECRET);
    Activity activity = getCurrentActivity();
    if (activity != null) {
      mStripe.authenticatePayment(activity, clientSecret);
    }
  }

  @ReactMethod
  public void confirmSetupIntent(final ReadableMap options, final Promise promise) {
    attachSetupResultActivityListener(promise);

    Activity activity = getCurrentActivity();
    if (activity != null) {
      mStripe.confirmSetupIntent(activity, extractConfirmSetupIntentParams(options));
    }
  }

  @ReactMethod
  public void authenticateSetupIntent(final ReadableMap options, final Promise promise) {
    attachSetupResultActivityListener(promise);

    String clientSecret = options.getString(CLIENT_SECRET);
    Activity activity = getCurrentActivity();
    if (activity != null) {
      mStripe.authenticateSetup(activity, clientSecret);
    }
  }


  @ReactMethod
  public void createPaymentMethod(final ReadableMap options, final Promise promise) {

    PaymentMethodCreateParams pmcp = extractPaymentMethodCreateParams(options);

    mStripe.createPaymentMethod(pmcp, new ApiResultCallback<PaymentMethod>() {

      @Override
      public void onError(Exception error) {
        promise.reject(toErrorCode(error), error.getMessage());
      }

      @Override
      public void onSuccess(PaymentMethod paymentMethod) {
        promise.resolve(convertPaymentMethodToWritableMap(paymentMethod));
      }
    });
  }


  @ReactMethod
  public void createSourceWithParams(final ReadableMap options, final Promise promise) {

    SourceParams sourceParams = extractSourceParams(options);

    ArgCheck.nonNull(sourceParams);

    mStripe.createSource(sourceParams, new ApiResultCallback<Source>() {
      @Override
      public void onError(Exception error) {
        promise.reject(toErrorCode(error));
      }

      @Override
      public void onSuccess(Source source) {
        if (Source.Flow.Redirect.equals(source.getFlow())) {
          Activity currentActivity = getCurrentActivity();
          if (currentActivity == null) {
            promise.reject(
              getErrorCode(mErrorCodes, "activityUnavailable"),
              getDescription(mErrorCodes, "activityUnavailable")
            );
          } else {
            mCreateSourcePromise = promise;
            mCreatedSource = source;
            String redirectUrl = source.getRedirect().getUrl();
            Intent browserIntent = new Intent(currentActivity, OpenBrowserActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
                .putExtra(OpenBrowserActivity.EXTRA_URL, redirectUrl);
            currentActivity.startActivity(browserIntent);
          }
        } else {
          promise.resolve(convertSourceToWritableMap(source));
        }
      }
    });
  }

  private ConfirmSetupIntentParams extractConfirmSetupIntentParams(final ReadableMap options) {
    ReadableMap paymentMethod = getMapOrNull(options, "paymentMethod");
    String paymentMethodId = getStringOrNull(options, "paymentMethodId");
    String returnURL = getStringOrNull(options, "returnURL");
    String clientSecret = options.getString("clientSecret");
    ConfirmSetupIntentParams csip = null;
    if (returnURL ==  null) {
      returnURL = "stripejs://use_stripe_sdk/return_url";
    }

    if (paymentMethod != null) {
      csip = ConfirmSetupIntentParams.create(extractPaymentMethodCreateParams(paymentMethod),
        clientSecret, returnURL);
    } else if (paymentMethodId != null) {
      csip = ConfirmSetupIntentParams.create(paymentMethodId, clientSecret, returnURL);
    }

    ArgCheck.nonNull(csip);
    csip.withShouldUseStripeSdk(true);
    return csip;
  }

  private ConfirmPaymentIntentParams extractConfirmPaymentIntentParams(final ReadableMap options) {
    ConfirmPaymentIntentParams cpip = null;
    String clientSecret = options.getString("clientSecret");

    ReadableMap paymentMethod = getMapOrNull(options, "paymentMethod");
    String paymentMethodId = getStringOrNull(options,"paymentMethodId");

    // ReadableMap source = options.getMap("source");
    // String sourceId = getStringOrNull(options,"sourceId");

    String returnURL = getStringOrNull(options, "returnURL");
    if (returnURL ==  null) {
      returnURL = "stripejs://use_stripe_sdk/return_url";
    }
    boolean savePaymentMethod = getBooleanOrNull(options, "savePaymentMethod", false);

    // TODO support extra params in each of the create methods below
    Map<String, Object> extraParams = null;

    // Create with Payment Method
    if (paymentMethod != null) {

      PaymentMethodCreateParams pmcp = extractPaymentMethodCreateParams(paymentMethod);
      cpip = ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams(pmcp, clientSecret, returnURL, savePaymentMethod, extraParams);

    // Create with Payment Method ID
    } else if (paymentMethodId != null) {

      cpip = ConfirmPaymentIntentParams.createWithPaymentMethodId(paymentMethodId, clientSecret, returnURL, savePaymentMethod, extraParams);

    // Create with Source
    /**
    Support for creating a Source while confirming a PaymentIntent is not being included
    at this time, however, for compatibility with existing saved Sources, you can still confirm
    a payment intent using a pre-existing Source by specifying its 'sourceId', as shown in the next
    branch
    */
    /*
    } else if (source != null) {
      SourceParams sourceParams = extractSourceParams(source);
      cpip = ConfirmPaymentIntentParams.createWithSourceParams(sourceParams, clientSecret, returnURL, savePaymentMethod, extraParams);
    */

    // Create with Source ID
    /**
    If you have a sourceId, pass it into the paymentMethodId parameter instead!
    The payment_method parameter of a payment intent is fully compatible with Sources.
    Reference: https://stripe.com/docs/api/payment_intents/confirm#confirm_payment_intent-payment_method
    */
    /*
    } else if (sourceId != null) {
      cpip = ConfirmPaymentIntentParams.createWithSourceId(sourceId, clientSecret, returnURL, savePaymentMethod, extraParams);
    */

    /**
    This branch can be used if the client secret refers to a payment intent that already
    has payment method information and just needs to be confirmed.
    */
    } else {
      cpip = ConfirmPaymentIntentParams.create(clientSecret, returnURL);
    }

    cpip.withShouldUseStripeSdk(true);

    return cpip;
  }

  private PaymentMethodCreateParams extractPaymentMethodCreateParams(final ReadableMap options) {

    ReadableMap cardParams = getMapOrNull(options, "card");
    ReadableMap billingDetailsParams = getMapOrNull(options, "billingDetails");
    ReadableMap metadataParams = getMapOrNull(options, "metadata");

    PaymentMethodCreateParams.Card card = null;
    PaymentMethod.BillingDetails billingDetails = null;
    Address address = null;
    Map<String, String> metadata = new HashMap<>();

    if (metadataParams != null) {
      ReadableMapKeySetIterator iter = metadataParams.keySetIterator();
      while (iter.hasNextKey()) {
        String key = iter.nextKey();
        metadata.put(key, metadataParams.getString(key));
      }
    }

    if (billingDetailsParams != null) {

      ReadableMap addressParams = getMapOrNull(billingDetailsParams, "address");

      if (addressParams != null) {
        address = new Address.Builder().
          setCity(getStringOrNull(addressParams, "city")).
          setCountry(getStringOrNull(addressParams, "country")).
          setLine1(getStringOrNull(addressParams, "line1")).
          setLine2(getStringOrNull(addressParams, "line2")).
          setPostalCode(getStringOrNull(addressParams, "postalCode")).
          setState(getStringOrNull(addressParams, "state")).
          build();
      }

      billingDetails = new PaymentMethod.BillingDetails.Builder().
        setAddress(address).
        setEmail(getStringOrNull(billingDetailsParams, "email")).
        setName(getStringOrNull(billingDetailsParams,"name")).
        setPhone(getStringOrNull(billingDetailsParams,"phone")).
        build();
    }

    if (cardParams != null) {
      String token = getStringOrNull(cardParams, "token");
      if (token != null) {
        card = PaymentMethodCreateParams.Card.create(token);
      } else {
        card = new PaymentMethodCreateParams.Card.Builder().
          setCvc(cardParams.getString("cvc")).
          setExpiryMonth(cardParams.getInt("expMonth")).
          setExpiryYear(cardParams.getInt("expYear")).
          setNumber(cardParams.getString("number")).
          build();
      }
    }

    return PaymentMethodCreateParams.create(
      card,
      billingDetails,
      metadata
    );
  }

  private SourceParams extractSourceParams(final ReadableMap options) {
    String sourceType = options.getString("type");
    SourceParams sourceParams = null;
    switch (sourceType) {
      case "alipay":
        sourceParams = SourceParams.createAlipaySingleUseParams(
          options.getInt("amount"),
          options.getString("currency"),
          getStringOrNull(options, "name"),
          getStringOrNull(options, "email"),
          options.getString("returnURL"));
        break;
      case "bancontact":
        sourceParams = SourceParams.createBancontactParams(
          options.getInt("amount"),
          options.getString("name"),
          options.getString("returnURL"),
          getStringOrNull(options, "statementDescriptor"),
          options.getString("preferredLanguage"));
        break;
      case "giropay":
        sourceParams = SourceParams.createGiropayParams(
          options.getInt("amount"),
          options.getString("name"),
          options.getString("returnURL"),
          getStringOrNull(options, "statementDescriptor"));
        break;
      case "ideal":
        sourceParams = SourceParams.createIdealParams(
          options.getInt("amount"),
          options.getString("name"),
          options.getString("returnURL"),
          getStringOrNull(options, "statementDescriptor"),
          getStringOrNull(options, "bank"));
        break;
      case "sepaDebit":
        sourceParams = SourceParams.createSepaDebitParams(
          options.getString("name"),
          options.getString("iban"),
          getStringOrNull(options, "addressLine1"),
          options.getString("city"),
          options.getString("postalCode"),
          options.getString("country"));
        break;
      case "sofort":
        sourceParams = SourceParams.createSofortParams(
          options.getInt("amount"),
          options.getString("returnURL"),
          options.getString("country"),
          getStringOrNull(options, "statementDescriptor"));
        break;
      case "threeDSecure":
        sourceParams = SourceParams.createThreeDSecureParams(
          options.getInt("amount"),
          options.getString("currency"),
          options.getString("returnURL"),
          options.getString("card"));
        break;
      case "card":
        sourceParams = SourceParams.createCardParams(Converters.createCard(options));
        break;
    }
    return sourceParams;
  }


  @SuppressLint("StaticFieldLeak")
  void processRedirect(@Nullable Uri redirectData) {
    if (mCreatedSource == null || mCreateSourcePromise == null) {

      return;
    }

    if (redirectData == null) {

      mCreateSourcePromise.reject(
        getErrorCode(mErrorCodes, "redirectCancelled"),
        getDescription(mErrorCodes, "redirectCancelled")
      );
      mCreatedSource = null;
      mCreateSourcePromise = null;
      return;
    }

    final String clientSecret = redirectData.getQueryParameter("client_secret");
    if (!mCreatedSource.getClientSecret().equals(clientSecret)) {
      mCreateSourcePromise.reject(
        getErrorCode(mErrorCodes, "redirectNoSource"),
        getDescription(mErrorCodes, "redirectNoSource")
      );
      mCreatedSource = null;
      mCreateSourcePromise = null;
      return;
    }

    final String sourceId = redirectData.getQueryParameter("source");
    if (!mCreatedSource.getId().equals(sourceId)) {
      mCreateSourcePromise.reject(
        getErrorCode(mErrorCodes, "redirectWrongSourceId"),
        getDescription(mErrorCodes, "redirectWrongSourceId")
      );
      mCreatedSource = null;
      mCreateSourcePromise = null;
      return;
    }

    final Promise promise = mCreateSourcePromise;

    // Nulls those properties to avoid processing them twice
    mCreatedSource = null;
    mCreateSourcePromise = null;

    new AsyncTask<Void, Void, Void>() {
      @Override
      protected Void doInBackground(Void... voids) {
        Source source = null;
        try {
          source = mStripe.retrieveSourceSynchronous(sourceId, clientSecret);
        } catch (Exception e) {

          return null;
        }

        switch (source.getStatus()) {
          case Chargeable:
          case Consumed:
            promise.resolve(convertSourceToWritableMap(source));
            break;
          case Canceled:
            promise.reject(
              getErrorCode(mErrorCodes, "redirectCancelled"),
              getDescription(mErrorCodes, "redirectCancelled")
            );
            break;
          case Pending:
          case Failed:
          default:
            promise.reject(
              getErrorCode(mErrorCodes, "redirectFailed"),
              getDescription(mErrorCodes, "redirectFailed")
            );
        }
        return null;
      }
    }.execute();
  }

}

Use the Above code (It is working fine with no build error) if you use Wallets Alone. I have removed some codes related to card payments after the updates because of confusion.

If you have time and know java, you can update the error codes manually.

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment