Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

payment methods #470

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
aaa6189
ios implementation
galkahana Apr 1, 2019
7cf9508
oops forgot card
galkahana Apr 1, 2019
060c418
theoretically, android
galkahana Apr 1, 2019
45dcd22
leftover
galkahana Apr 1, 2019
5398896
revert androidx and later stripe version
galkahana Apr 1, 2019
7ccce92
oops
galkahana Apr 1, 2019
0366476
oh come on
galkahana Apr 1, 2019
6a1fcb0
handle alternative customer session behavior
galkahana Apr 1, 2019
89cfabe
use khealth stripe version so can avoid androidX but correct add card…
galkahana Apr 1, 2019
486b3f9
please work
galkahana Apr 1, 2019
7e627e6
apple pay
galkahana Apr 2, 2019
65df4ad
add resultType to return payload to identity what was returned
galkahana Apr 4, 2019
f611475
new stripe
galkahana Apr 15, 2019
67ce805
remove apple pay merchant id reduncency
galkahana Apr 17, 2019
0603631
merge with remote master
galkahana Apr 22, 2019
75b2289
add documentation
galkahana Apr 22, 2019
d35b957
upgrade gradle to support latest sdk
galkahana Apr 22, 2019
8c708bb
should improve ci now
galkahana Apr 25, 2019
5aa0028
payment methods testing attempts
galkahana Apr 26, 2019
46911ee
put back ios settings
galkahana Apr 27, 2019
d4f6e74
use fetch instead
galkahana Apr 27, 2019
2e9ca12
make backend api url variable
galkahana Apr 27, 2019
3cfb103
condition test full run based on backend_url existance
galkahana Apr 27, 2019
edd02b4
Merge pull request #1 from galkahana/testing/adding-payment-methods
galkahana Apr 27, 2019
f6119a4
Merge pull request #2 from galkahana/master
galkahana Apr 27, 2019
7812944
Merge branch 'master' into master
galkahana Apr 27, 2019
964b559
jimp issue now resolved by author
galkahana Apr 27, 2019
ac3213d
update android stripe to google-pay-export to enjoy new content...wit…
galkahana May 2, 2019
56eeaa4
use the newly released android sdk
galkahana May 8, 2019
dcd8f76
have to upgrade to min sdk 19
galkahana May 8, 2019
7e0461e
and in all other places
galkahana May 8, 2019
b339081
ok. might need a repo update here
galkahana May 8, 2019
132147f
firebase sais no longer supporting ios 9 and i dont see usages...how …
galkahana May 8, 2019
31d329c
try a later version of appium that wont have the other issue
galkahana May 9, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ matrix:
- tools
- build-tools-25.0.0
- build-tools-26.0.2
- build-tools-28.0.3
- android-21
- android-26
- android-28
- sys-img-armeabi-v7a-android-21
- extra-android-m2repository
- extra-google-m2repository
Expand Down
14 changes: 7 additions & 7 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apply plugin: 'com.android.library'

def DEFAULT_COMPILE_SDK_VERSION = 26
def DEFAULT_TARGET_SDK_VERSION = 26
def DEFAULT_BUILD_TOOLS_VERSION = '26.0.2'
def DEFAULT_COMPILE_SDK_VERSION = 28
def DEFAULT_TARGET_SDK_VERSION = 28
def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3'
def DEFAULT_GOOGLE_PLAY_SERVICES_VERSION = '11.8.0'
def DEFAULT_FIREBASE_MESSAGING_VERSION = '11.8.0'

Expand Down Expand Up @@ -34,11 +34,11 @@ dependencies {

implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.facebook.react:react-native:+'
implementation 'com.android.support:support-v4:27.1.0'
implementation 'com.android.support:appcompat-v7:27.1.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation "com.google.android.gms:play-services-wallet:$googlePlayServicesVersion"
implementation "com.google.firebase:firebase-core:$firebaseVersion"
implementation 'com.stripe:stripe-android:8.1.0'
implementation "com.github.khealth:stripe-android:google-pay-SNAPSHOT"
galkahana marked this conversation as resolved.
Show resolved Hide resolved
implementation 'com.github.tipsi:CreditCardEntry:1.5.1'
}

Expand All @@ -47,4 +47,4 @@ repositories {
url 'https://maven.google.com/'
name 'Google'
}
}
}
Empty file added android/gradle.properties
Empty file.
28 changes: 28 additions & 0 deletions android/src/main/java/com/gettipsi/stripe/DirectKeyProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.gettipsi.stripe;

import com.stripe.android.EphemeralKeyProvider;
import com.stripe.android.EphemeralKeyUpdateListener;

import com.gettipsi.stripe.StripeModule;
import com.facebook.react.bridge.ReadableMap;


public class DirectKeyProvider implements EphemeralKeyProvider {

private String mRawKey;

public DirectKeyProvider(String rawKey) {
mRawKey = rawKey;
}

@Override
public void createEphemeralKey(String apiVersion,
final EphemeralKeyUpdateListener keyUpdateListener) {
if(mRawKey == null) {
StripeModule.getInstance().delayEphermalKeyResolution(apiVersion, keyUpdateListener);
}
else {
keyUpdateListener.onKeyUpdate(mRawKey);
}
}
}
140 changes: 125 additions & 15 deletions android/src/main/java/com/gettipsi/stripe/StripeModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.gettipsi.stripe.dialog.AddCardDialogFragment;
import com.gettipsi.stripe.util.ArgCheck;
import com.gettipsi.stripe.util.Converters;
Expand All @@ -26,6 +27,13 @@
import com.stripe.android.model.Source;
import com.stripe.android.model.SourceParams;
import com.stripe.android.model.Token;
import com.stripe.android.CustomerSession;
import com.stripe.android.model.Customer;
import com.stripe.android.view.PaymentMethodsActivity;
import com.stripe.android.PaymentConfiguration;
import com.stripe.android.EphemeralKeyUpdateListener;
import com.stripe.android.StripeError;
import com.stripe.android.view.PaymentMethodsActivityStarter;

import static com.gettipsi.stripe.Errors.*;
import static com.gettipsi.stripe.util.Converters.convertSourceToWritableMap;
Expand All @@ -37,11 +45,14 @@
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.gettipsi.stripe.util.InitializationOptions.EPHEMERAL_KEY;

public class StripeModule extends ReactContextBaseJavaModule {

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

private static final int REQUEST_CODE_SELECT_SOURCE = 55;

private static StripeModule sInstance = null;

public static StripeModule getInstance() {
Expand All @@ -53,7 +64,7 @@ public Stripe getStripe() {
}

@Nullable
private Promise mCreateSourcePromise;
private Promise mCurrentPromise;

@Nullable
private Source mCreatedSource;
Expand All @@ -63,14 +74,35 @@ public Stripe getStripe() {
private PayFlow mPayFlow;
private ReadableMap mErrorCodes;

private EphemeralKeyUpdateListener mEphemeralKeyReceiver;

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);
}
if (requestCode == REQUEST_CODE_SELECT_SOURCE) {
super.onActivityResult(activity, requestCode, resultCode, data);
if(resultCode == Activity.RESULT_OK) {
String selectedSource = data.getStringExtra(PaymentMethodsActivity.EXTRA_SELECTED_PAYMENT);
Source source = Source.fromString(selectedSource);
WritableMap result = convertSourceToWritableMap(source);
result.putString("resultType", "STPSource");
mCurrentPromise.resolve(result);
}
else {
mCurrentPromise.reject(
getErrorCode(mErrorCodes, "cancelled"),
getDescription(mErrorCodes, "cancelled")
);
}
mCurrentPromise = null;
} else {
boolean handled = getPayFlow().onActivityResult(activity, requestCode, resultCode, data);
if (!handled) {
super.onActivityResult(activity, requestCode, resultCode, data);
}

}
}
};

Expand Down Expand Up @@ -102,6 +134,7 @@ public void init(@NonNull ReadableMap options, @NonNull ReadableMap errorCodes)
mPublicKey = newPubKey;
mStripe = new Stripe(getReactApplicationContext(), mPublicKey);
getPayFlow().setPublishableKey(mPublicKey);
PaymentConfiguration.init(mPublicKey);
}

if (newAndroidPayMode != null) {
Expand Down Expand Up @@ -133,6 +166,19 @@ private static int androidPayModeToEnvironment(@NonNull String androidPayMode) {
return ANDROID_PAY_MODE_TEST.equals(androidPayMode.toLowerCase()) ? WalletConstants.ENVIRONMENT_TEST : WalletConstants.ENVIRONMENT_PRODUCTION;
}

public void delayEphermalKeyResolution(String apiVersion,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: Ephermal 👉 Ephemeral

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void delayEphermalKeyResolution(String apiVersion,
public void delayEphemeralKeyResolution(String apiVersion,

final EphemeralKeyUpdateListener keyUpdateListener) {
mEphemeralKeyReceiver = keyUpdateListener;
mCurrentPromise.resolve(apiVersion);
mCurrentPromise = null;
}

private void launchWithCustomer() {
Activity currentActivity = getCurrentActivity();
new PaymentMethodsActivityStarter(currentActivity).startForResult(REQUEST_CODE_SELECT_SOURCE);
}


@ReactMethod
public void deviceSupportsAndroidPay(final Promise promise) {
getPayFlow().deviceSupportsAndroidPay(false, promise);
Expand Down Expand Up @@ -297,7 +343,7 @@ public void onSuccess(Source source) {
getDescription(mErrorCodes, "activityUnavailable")
);
} else {
mCreateSourcePromise = promise;
mCurrentPromise = promise;
mCreatedSource = source;
String redirectUrl = source.getRedirect().getUrl();
Intent browserIntent = new Intent(currentActivity, OpenBrowserActivity.class)
Expand All @@ -312,50 +358,114 @@ public void onSuccess(Source source) {
});
}

private void retrieveCustomerSession() {
CustomerSession.getInstance().retrieveCurrentCustomer(
new CustomerSession.CustomerRetrievalListener() {
@Override
public void onCustomerRetrieved(Customer customer) {
// got customer, continue by launching the payment methods dialog
launchWithCustomer();
}

@Override
public void onError(int httpCode, String errorMessage, StripeError error) {
// failed to get customer
mCurrentPromise.reject("StripeError",errorMessage);
mCurrentPromise = null;
}
});
}

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

String ephemeralKey = Converters.getStringOrNull(params, EPHEMERAL_KEY);
mCurrentPromise = promise;

CustomerSession.initCustomerSession(
getReactApplicationContext(),
new DirectKeyProvider(ephemeralKey));

if(mCurrentPromise != null) {
retrieveCustomerSession();
}
else {
// this means that there was already an attempt to retrieve the key and promise was resolved.
// there's difference in behvior between versions, and nulling the receiver is a good way of
// knowing to restart later
mEphemeralKeyReceiver = null;
}

} catch (Exception e) {
promise.reject(toErrorCode(e), e.getMessage());
}
}

@ReactMethod
public void completePaymentRequestWithPaymentMethods(String ephemeralKey, final Promise promise) {
mCurrentPromise = promise;
if(mEphemeralKeyReceiver != null) {
mEphemeralKeyReceiver.onKeyUpdate(ephemeralKey);
mEphemeralKeyReceiver = null;
}
else {
// didnt have receiver so retrieve the session now
CustomerSession.endCustomerSession();
CustomerSession.initCustomerSession(
getReactApplicationContext(),
new DirectKeyProvider(ephemeralKey));
retrieveCustomerSession();
}
}

void processRedirect(@Nullable Uri redirectData) {
if (mCreatedSource == null || mCreateSourcePromise == null) {
if (mCreatedSource == null || mCurrentPromise == null) {

return;
}

if (redirectData == null) {

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

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

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

final Promise promise = mCreateSourcePromise;
final Promise promise = mCurrentPromise;

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

new AsyncTask<Void, Void, Void>() {
@Override
Expand Down
25 changes: 14 additions & 11 deletions android/src/main/java/com/gettipsi/stripe/util/Converters.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public static Collection<String> getAllowedShippingCountryCodes(final ReadableMa
return allowedCountryCodesForShipping;
}

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

Expand Down Expand Up @@ -199,7 +199,8 @@ public static Card createCard(final ReadableMap cardData) {
getValue(cardData, "funding"),
getValue(cardData, "country"),
getValue(cardData, "currency"),
getValue(cardData, "id")
getValue(cardData, "id"),
null
);
}

Expand Down Expand Up @@ -367,7 +368,7 @@ public static void pushRightTypeToMap(@NonNull WritableMap map, @NonNull String
}
}

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

if (address == null) return result;
Expand All @@ -391,14 +392,16 @@ public static WritableMap convertAddressToWritableMap(final UserAddress address)

public static BankAccount createBankAccount(ReadableMap accountData) {
BankAccount account = new BankAccount(
// required fields only
accountData.getString("accountNumber"),
accountData.getString("countryCode"),
accountData.getString("currency"),
getValue(accountData, "routingNumber", "")
accountData.getString("accountNumber"),
getValue(accountData, "accountHolderName"),
getValue(accountData, "accountHolderType"),
null,
accountData.getString("countryCode"),
accountData.getString("currency"),
null,
null,
getValue(accountData, "routingNumber", "")
);
account.setAccountHolderName(getValue(accountData, "accountHolderName"));
account.setAccountHolderType(getValue(accountData, "accountHolderType"));

return account;
}
Expand All @@ -413,7 +416,7 @@ public static void putIfNotEmpty(final WritableMap map, final String key, final
}
}

public static UserAddress getBillingAddress(PaymentData paymentData) {
public static UserAddress getBillingAddress(PaymentData paymentData) {
if (paymentData != null && paymentData.getCardInfo() != null) {
return paymentData.getCardInfo().getBillingAddress();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ public abstract class InitializationOptions {
public static final String ANDROID_PAY_MODE_KEY = "androidPayMode";
public static final String ANDROID_PAY_MODE_PRODUCTION = "production";
public static final String ANDROID_PAY_MODE_TEST = "test";
public static final String EPHEMERAL_KEY = "ephemeralKey";

}
Loading