7

I am working to integrate the Stripe payment gateway in an Android app that I am developing. I have followed the steps listed on https://stripe.com/docs/mobile/android.

When I try to create a new card I get errors.

I run this in my activity

saveButton.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View view) {
  Card card = new Card(
   // Get values from the form
   cardNumber.getText().toString(),
   getInteger(monthSpinner),
   getInteger(yearSpinner),
   cvc.getText().toString()
  );
 }
});

Which uses the Card model.

Card model

package com.stripe.android.model;

import com.stripe.android.util.DateUtils;
import com.stripe.android.util.TextUtils;

public class Card extends com.stripe.model.StripeObject {
 String number;
 String cvc;
 Integer expMonth;
 Integer expYear;
 String name;
 String addressLine1;
 String addressLine2;
 String addressCity;
 String addressState;
 String addressZip;
 String addressCountry;
 String last4;
 String type;
 String fingerprint;
 String country;

 public String getNumber() {
    return number;
 }

 public void setNumber(String number) {
    this.number = number;
 }

 public String getCVC() {
    return cvc;
 }

 public void setCVC(String cvc) {
    this.cvc = cvc;
 }

 public Integer getExpMonth() {
    return expMonth;
 }

 public void setExpMonth(Integer expMonth) {
    this.expMonth = expMonth;
 }

 public Integer getExpYear() {
    return expYear;
 }

 public void setExpYear(Integer expYear) {
    this.expYear = expYear;
 }

 public String getName() {
    return name;
 }

 public void setName(String name) {
    this.name = name;
 }

 public String getAddressLine1() {
    return addressLine1;
 }

 public void setAddressLine1(String addressLine1) {
    this.addressLine1 = addressLine1;
 }

 public String getAddressLine2() {
    return addressLine2;
 }

 public void setAddressLine2(String addressLine2) {
    this.addressLine2 = addressLine2;
 }

 public String getAddressCity() {
    return addressCity;
 }

 public void setAddressCity(String addressCity) {
    this.addressCity = addressCity;
 }

 public String getAddressZip() {
    return addressZip;
 }

 public void setAddressZip(String addressZip) {
    this.addressZip = addressZip;
 }

 public String getAddressState() {
    return addressState;
 }

 public void setAddressState(String addressState) {
    this.addressState = addressState;
 }

 public String getAddressCountry() {
    return addressCountry;
 }

 public void setAddressCountry(String addressCountry) {
    this.addressCountry = addressCountry;
 }

 public String getLast4() {
    if (!TextUtils.isBlank(last4)) {
        return last4;
    }
    if (number != null && number.length() > 4) {
        return number.substring(number.length() - 4, number.length());
    }
    return null;
 }

 public String getType() {
    if (TextUtils.isBlank(type) && !TextUtils.isBlank(number)) {
        if (TextUtils.hasAnyPrefix(number, "34", "37")) {
            return "American Express";
        } else if (TextUtils.hasAnyPrefix(number, "60", "62", "64", "65")) {
            return "Discover";
        } else if (TextUtils.hasAnyPrefix(number, "35")) {
            return "JCB";
        } else if (TextUtils.hasAnyPrefix(number, "30", "36", "38", "39")) {
            return "Diners Club";
        } else if (TextUtils.hasAnyPrefix(number, "4")) {
            return "Visa";
        } else if (TextUtils.hasAnyPrefix(number, "5")) {
            return "MasterCard";
        } else {
            return "Unknown";
        }
    }
    return type;
 }

 public String getFingerprint() {
    return fingerprint;
 }

 public String getCountry() {
    return country;
 }

 public Card(String number, Integer expMonth, Integer expYear, String cvc, String name, String addressLine1, String addressLine2, String addressCity, String addressState, String addressZip, String addressCountry, String last4, String type, String fingerprint, String country) {
    this.number = TextUtils.nullIfBlank(normalizeCardNumber(number));
    this.expMonth = expMonth;
    this.expYear = expYear;
    this.cvc = TextUtils.nullIfBlank(cvc);
    this.name = TextUtils.nullIfBlank(name);
    this.addressLine1 = TextUtils.nullIfBlank(addressLine1);
    this.addressLine2 = TextUtils.nullIfBlank(addressLine2);
    this.addressCity = TextUtils.nullIfBlank(addressCity);
    this.addressState = TextUtils.nullIfBlank(addressState);
    this.addressZip = TextUtils.nullIfBlank(addressZip);
    this.addressCountry = TextUtils.nullIfBlank(addressCountry);
    this.last4 = TextUtils.nullIfBlank(last4);
    this.type = TextUtils.nullIfBlank(type);
    this.fingerprint = TextUtils.nullIfBlank(fingerprint);
    this.country = TextUtils.nullIfBlank(country);
 }

 public Card(String number, Integer expMonth, Integer expYear, String cvc, String name, String addressLine1, String addressLine2, String addressCity, String addressState, String addressZip, String addressCountry) {
    this(number, expMonth, expYear, cvc, name, addressLine1, addressLine2, addressCity, addressState, addressZip, addressCountry, null, null, null, null);
 }

 public Card(String number, Integer expMonth, Integer expYear, String cvc) {
    this(number, expMonth, expYear, cvc, null, null, null, null, null, null, null, null, null, null, null);
    this.type = getType();
 }

 public boolean validateCard() {
    if (cvc == null) {
        return validateNumber() && validateExpiryDate();
    } else {
        return validateNumber() && validateExpiryDate() && validateCVC();
    }
 }

 public boolean validateNumber() {
    if (TextUtils.isBlank(number)) {
        return false;
    }

    String rawNumber = number.trim().replaceAll("\\s+|-", "");
    if (TextUtils.isBlank(rawNumber)
            || !TextUtils.isWholePositiveNumber(rawNumber)
            || !isValidLuhnNumber(rawNumber)) {
        return false;
    }

    if (!"American Express".equals(type) && rawNumber.length() != 16) {
        return false;
    }

    if ("American Express".equals(type) && rawNumber.length() != 15) {
        return false;
    }

    return true;
 }

 public boolean validateExpiryDate() {
    if (!validateExpMonth()) {
        return false;
    }
    if (!validateExpYear()) {
        return false;
    }
    return !DateUtils.hasMonthPassed(expYear, expMonth);
 }

 public boolean validateExpMonth() {
    if (expMonth == null) {
        return false;
    }
    return (expMonth >= 1 && expMonth <= 12);
 }

 public boolean validateExpYear() {
    if (expYear == null) {
        return false;
    }
    return !DateUtils.hasYearPassed(expYear);
 }

 public boolean validateCVC() {
    if (TextUtils.isBlank(cvc)) {
        return false;
    }
    String cvcValue = cvc.trim();

    boolean validLength = ((type == null && cvcValue.length() >= 3 && cvcValue.length() <= 4) ||
            ("American Express".equals(type) && cvcValue.length() == 4) ||
            (!"American Express".equals(type) && cvcValue.length() == 3));


    if (!TextUtils.isWholePositiveNumber(cvcValue) || !validLength) {
        return false;
    }
    return true;
 }

 private boolean isValidLuhnNumber(String number) {
    boolean isOdd = true;
    int sum = 0;

    for (int index = number.length() - 1; index >= 0; index--) {
        char c = number.charAt(index);
        if (!Character.isDigit(c)) {
            return false;
        }
        int digitInteger = Integer.parseInt("" + c);
        isOdd = !isOdd;

        if (isOdd) {
            digitInteger *= 2;
        }

        if (digitInteger > 9) {
            digitInteger -= 9;
        }

        sum += digitInteger;
    }

    return sum % 10 == 0;
 }

 private String normalizeCardNumber(String number) {
  if (number == null) {
    return null;
  }
  return number.trim().replaceAll("\\s+|-", "");
 }
}

This is the error I am getting.

enter image description here

This is new to me. What can I do to resolve this?

localhost
  • 1,062
  • 3
  • 15
  • 35
  • 1
    Are you sure that you are using the right `Card` implementation? For example, do you have the right `import`? You have a constructor in the code above that would appear to match that parameter list, which suggests that your IDE is thinking of some other `Card`. – CommonsWare Jan 03 '14 at 18:38
  • I have thought of that. I believe that the import is correct. – localhost Jan 03 '14 at 18:42
  • 1
    Please don't paste a giant code dump of the section that has nothing to do with the issue. What we *do* need to see are the constructor(s) for `Card` and the actual imports for your activity. – chrylis -cautiouslyoptimistic- Jan 24 '14 at 16:29
  • 1
    @Chrylis, your comment seems needlessly harsh. How many times does a poster get chastised for not giving enough details? And, in any case, the code and screenshot told me that I had found exactly the same problem I was trying to solve. Of course, after that, it took an hour of hard work to try a bunch of options suggested below and elsewhere and then, finally, stumble on the 30-second fix. (See my answer below.) – Anne Gunn Oct 01 '14 at 22:47
  • @localhost, i'm doing the same, i have one doubt, after getting the token where payment will be done, either from client side or server side – Androidcuckoo Jan 30 '15 at 06:48
  • i think they removed constructors from new update. u get any solution for this ? i also facing this issue... – Jithish P N Feb 04 '16 at 10:17

8 Answers8

5

Since this is a top hit on Google search for 'How to integrate Stripe to Android Studio' and since Android studio removed the import module this is how I solved the import.

  • Right click on the project and select > New > Module
  • In your directories copy contents under Stripe > Stripe folder to the module folder (You should see a newly created folder. Delete the contents of this new folder and paste the contents of Stripe > Stripe)
  • Back to Android Studio navigate to build.gradle under src add compile project(":stripe") under dependencies.
  • Refresh your gradle.

EDIT 1 Since posting this answer some changes have happened. If you would wish to add stripe into your project do so via Maven. Just add this line to your app's build.gradle inside the dependencies section:

compile 'com.stripe:stripe-android:2.0.2'

EDIT 2 It's now implementation and not compile.

implementation 'com.stripe:stripe-android:6.1.2'

You can get more details here : https://stripe.com/docs/mobile/android

Kennedy Nyaga
  • 3,455
  • 1
  • 26
  • 25
4

Okay, so I believe what's happening is that you don't have access to the proper Card() constructor because you haven't set things up properly.

  1. Go to the github page and download the link [for the library]https://github.com/stripe/stripe-android. Unpack that folder and keep it handy.

  2. Now, go into android studio and hit 'import module'. Navigate into that stripe-android directory that you just unzipped, and hit okay. Make sure you only have 'stripe' checked when importing, and not 'example' (only 'example' will be checked by default: fix this.)

  3. Copy the jarfile stripe-java-1.12.0.jar to the directory :libs in your project (where you'd have other libraries). That jarfile should show up under the new 'stripe' directory in android studio.

  4. Go into your src directory and find your app's build.gradle. You're going to want to add, under dependencies:

    compile project(":stripe")

You may run into an error at some point saying that you need a newer version of build tools to build the project. If that's so, just start rummaging through the gradle files and changing numbers until it builds. That's what I do, at least.

Hope this helps!

(p.s: remember to include com.stripe.android.* and not com.stripe.*!)

Edit: Just ran into a new problem, and it turns out you should skip step 3. It'll cause dex to freak out that the same class is being defined in the same jarfile twice. So don't do it.

user3749398
  • 81
  • 1
  • 5
3

If you're not using Gradle then below is how I got it to work:

  1. Download the zip from the stripe GitHub (stripe-android-master)
  2. Import JUST the stripe folder as a module into your project. You shouldn't have to do anything fancy here.
  3. It added to my project as "main". Go into Project Structure -> modules and add "main" as a module dependency to your working module
  4. Click on the "main" (stripe) module and click the "Export" checkbox on the lib so that your working module has access to it
  5. ????
  6. Profit
Kyle Venn
  • 4,597
  • 27
  • 41
0

I just had exactly the same problem as the OP. I was importing some variant of Stripe code but did not have the multi-argument constructor or any of the specific methods I was looking for, so I clearly was not importing what I wanted/needed.

I tried many of the Import Module or Add Library incantations found here or elsewhere. Finally, about to give up, I tried that most desperate of all measures: RTFM. Or, in this case the README.md that came with the project I downloaded.

There, for Android Studio users, was the trivial solution that actually worked for me:

No need to clone the repository or download any files -- just add this line to your app's `build.gradle` inside the `dependencies` section:

    compile 'com.stripe:stripe-android:+'

It worked like a charm.

Ironically, you don't have to download or clone to USE the library but so far the only way I know to get the README.md is to download the library files from here: https://stripe.com/docs/mobile/android

Caveat: I wrote the above as soon as Android Studio started importing the right library and my IDE compilation errors went away. But as soon as I tried to actually build and run my code, I ran head on into the multiple dex horror for, I believe, pulling in multiple copies of the gson library that Stripe depends on and my code already uses. Sigh. I DID fix the problem by removing my local copy of the gson jar and, I presume, depending on the one located with the stripe package. Just deleting all the bin/intermediate/generated folders wasn't good enough. I'm not real happy with this solution but may live with it for now.

Community
  • 1
  • 1
Anne Gunn
  • 2,347
  • 1
  • 28
  • 42
0

well, to use Stripe, you don't need to download anything from github or Stripe.com. Here is how I do it. Since I can't post image (with only 1 reputation), its destribed as below:
1. Right-click on your project
2. click Open Module Settings
3. Click dependencies
4. click add
5. click choose library dependencies
6. input "stripe"
7. click search
8. click com.stripe:stripe.android 1.0.0
9. click OK.

Tony Hinkle
  • 4,706
  • 7
  • 23
  • 35
Kai Wang
  • 3,303
  • 1
  • 31
  • 27
0
Card card = new Card(cardNumber.getText().toString(),getInteger(this.monthSpinner),getInteger(this.yearSpinner),cvc.getText().toString());

use this, the actual and formal parameter are not matching.

Thanks

CocoCrisp
  • 807
  • 1
  • 9
  • 26
0

For all those who could not find a fix with above answers.Here's what I did when I faced the same situation.

  • Make sure you have gradle dependency as: compile 'com.stripe:stripe-android:+'
  • In the class where you are using Card model-delete all the stripe import statements and then click on the Card and make sure to import from -

"com.stripe.android.model"

Hope that fixes the issue.Thanks

Abhinav Pawar
  • 421
  • 3
  • 12
0

monthSpinner.getselecteditem() use this and pass it to the getinteger. Same for the yearSpinner..