feat(android): add initial objects
parent
d449073667
commit
80914bdede
@ -0,0 +1,27 @@
|
||||
dependencies {
|
||||
implementation 'com.stripe:stripe-android:8.5.0'
|
||||
compile 'com.google.android.gms:play-services-wallet:16.0.1'
|
||||
|
||||
/* Cordova doesn't support AndroidX support libraries yet */
|
||||
compile 'com.android.support:support-v4:28.0.0'
|
||||
compile 'com.android.support:appcompat-v7:28.0.0'
|
||||
|
||||
/* Needed for RxAndroid */
|
||||
implementation 'io.reactivex:rxandroid:1.2.1'
|
||||
implementation 'io.reactivex:rxjava:1.3.0'
|
||||
|
||||
/* Needed for Rx Bindings on views */
|
||||
implementation 'com.jakewharton.rxbinding:rxbinding:0.4.0'
|
||||
|
||||
/* Used for server calls */
|
||||
implementation 'com.squareup.okio:okio:1.15.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
|
||||
|
||||
/* Used to make Retrofit easier and GSON & Rx-compatible*/
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
implementation 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
|
||||
|
||||
/* Used to debug your Retrofit connections */
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
package com.rolamix.plugins.stripe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.os.Build;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import com.google.android.gms.common.api.ApiException;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
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.TransactionInfo;
|
||||
import com.google.android.gms.wallet.Wallet;
|
||||
import com.google.android.gms.wallet.WalletConstants;
|
||||
import com.stripe.android.CardUtils;
|
||||
import com.stripe.android.SourceCallback;
|
||||
import com.stripe.android.Stripe;
|
||||
import com.stripe.android.TokenCallback;
|
||||
import com.stripe.android.model.AccountParams;
|
||||
import com.stripe.android.model.BankAccount;
|
||||
import com.stripe.android.model.Card;
|
||||
import com.stripe.android.model.Source;
|
||||
import com.stripe.android.model.SourceParams;
|
||||
import com.stripe.android.model.Token;
|
||||
import com.stripe.android.view.CardInputWidget;
|
||||
|
||||
// https://stripe.com/docs/mobile/android
|
||||
// https://github.com/stripe/stripe-android
|
||||
// https://github.com/zyra/cordova-plugin-stripe/blob/v2/src/android/CordovaStripe.java
|
||||
// https://github.com/stripe/stripe-connect-rocketrides/blob/master/server/routes/api/rides.js
|
||||
|
||||
public class StripePaymentsPlugin extends CordovaPlugin {
|
||||
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
private String publishableKey;
|
||||
|
||||
private static final int LOAD_PAYMENT_DATA_REQUEST_CODE = 9972;
|
||||
|
||||
public static final String ACTION_SET_KEY = "setKey";
|
||||
|
||||
public static final String ACTION_SET_NAME = "setName";
|
||||
|
||||
public static final String ACTION_PICK = "pick";
|
||||
|
||||
public static final String ACTION_PICK_AND_STORE = "pickAndStore";
|
||||
|
||||
public static final String ACTION_HAS_PERMISSION = "hasPermission";
|
||||
|
||||
private static final String LOG_TAG = "FileStackPlugin";
|
||||
|
||||
public StripePaymentsPlugin() {}
|
||||
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
stripeInstance = new Stripe(webView.getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArray of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false otherwise.
|
||||
*/
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
this.executeArgs = args;
|
||||
this.action = action;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || action.equals(ACTION_HAS_PERMISSION)) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, hasPermission()));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || action.equals(ACTION_SET_KEY) || action.equals(ACTION_SET_NAME)) {
|
||||
execute();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (hasPermission()) {
|
||||
execute();
|
||||
} else {
|
||||
requestPermission();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPermission() {
|
||||
return cordova.hasPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
private void requestPermission() {
|
||||
cordova.requestPermission(this, 0, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
|
||||
for (int r : grantResults) {
|
||||
if (r == PackageManager.PERMISSION_DENIED) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "User has denied permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
execute();
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
final FileStackPlugin cdvPlugin = this;
|
||||
this.cordova.getThreadPool().execute(() -> {
|
||||
try {
|
||||
if (ACTION_SET_KEY.equals(cdvPlugin.getAction())) {
|
||||
this.apiKey = cdvPlugin.getArgs().getString(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Context context = cordova.getActivity().getApplicationContext();
|
||||
Intent intent = new Intent(context, FsActivity.class);
|
||||
Config config = new Config(this.apiKey);
|
||||
intent.putExtra(FsConstants.EXTRA_CONFIG, config);
|
||||
intent.putExtra(FsConstants.EXTRA_AUTO_UPLOAD, true);
|
||||
if (ACTION_PICK.equals(cdvPlugin.getAction()) || ACTION_PICK_AND_STORE.equals(cdvPlugin.getAction())) {
|
||||
parseGlobalArgs(intent, cdvPlugin.getArgs());
|
||||
if (ACTION_PICK_AND_STORE.equals(cdvPlugin.getAction())) {
|
||||
parseStoreArgs(intent, cdvPlugin.getArgs());
|
||||
}
|
||||
cordova.startActivityForResult(cdvPlugin, intent, REQUEST_FILESTACK);
|
||||
}
|
||||
}
|
||||
catch(JSONException exception) {
|
||||
cdvPlugin.getCallbackContext().error("cannot parse json");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_FILESTACK) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
ArrayList<Selection> selections = data.getParcelableArrayListExtra(FsConstants.EXTRA_SELECTION_LIST);
|
||||
try{
|
||||
callbackContext.success(toJSON(selections));
|
||||
}
|
||||
catch(JSONException exception) {
|
||||
callbackContext.error("json exception");
|
||||
}
|
||||
} else {
|
||||
callbackContext.error("nok");
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseGlobalArgs(Intent intent, JSONArray args) throws JSONException {
|
||||
if (!args.isNull(0)) {
|
||||
intent.putExtra("mimetype", parseJSONStringArray(args.getJSONArray(0)));
|
||||
}
|
||||
if (!args.isNull(1)) {
|
||||
intent.putExtra("services", parseJSONStringArray(args.getJSONArray(1)));
|
||||
}
|
||||
if (!args.isNull(2)) {
|
||||
intent.putExtra("multiple", args.getBoolean(2));
|
||||
}
|
||||
if (!args.isNull(3)) {
|
||||
intent.putExtra("maxFiles", args.getInt(3));
|
||||
}
|
||||
if (!args.isNull(4)) {
|
||||
intent.putExtra("maxSize", args.getInt(4));
|
||||
}
|
||||
}
|
||||
|
||||
public void parseStoreArgs(Intent intent, JSONArray args) throws JSONException {
|
||||
if (!args.isNull(5)) {
|
||||
intent.putExtra("location", args.getString(5));
|
||||
}
|
||||
if (!args.isNull(6)) {
|
||||
intent.putExtra("path", args.getString(6));
|
||||
}
|
||||
if (!args.isNull(7)) {
|
||||
intent.putExtra("container", args.getString(7));
|
||||
}
|
||||
if (!args.isNull(8)) {
|
||||
intent.putExtra("access", args.getString(8));
|
||||
}
|
||||
}
|
||||
|
||||
public String[] parseJSONStringArray(JSONArray jSONArray) throws JSONException {
|
||||
String[] a = new String[jSONArray.length()];
|
||||
for(int i = 0; i < jSONArray.length(); i++){
|
||||
a[i] = jSONArray.getString(i);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
public JSONArray toJSON(ArrayList<Selection> selections) throws JSONException {
|
||||
JSONArray res = new JSONArray();
|
||||
for (Selection selection : selections) {
|
||||
JSONObject f = new JSONObject();
|
||||
f.put("provider", selection.getProvider());
|
||||
f.put("url", selection.getUri());
|
||||
f.put("filename", selection.getName());
|
||||
f.put("mimetype", selection.getMimeType());
|
||||
f.put("localPath", selection.getPath());
|
||||
f.put("size", selection.getSize());
|
||||
|
||||
res.put(f);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public JSONArray getArgs() {
|
||||
return this.executeArgs;
|
||||
}
|
||||
|
||||
public CallbackContext getCallbackContext() {
|
||||
return this.callbackContext;
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package com.stripe.example.service;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Size;
|
||||
|
||||
import com.stripe.android.EphemeralKeyProvider;
|
||||
import com.stripe.android.EphemeralKeyUpdateListener;
|
||||
import com.stripe.example.module.RetrofitFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Retrofit;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.functions.Action1;
|
||||
import rx.schedulers.Schedulers;
|
||||
import rx.subscriptions.CompositeSubscription;
|
||||
|
||||
/**
|
||||
* An implementation of {@link EphemeralKeyProvider} that can be used to generate
|
||||
* ephemeral keys on the backend.
|
||||
*/
|
||||
public class ExampleEphemeralKeyProvider implements EphemeralKeyProvider {
|
||||
|
||||
private @NonNull CompositeSubscription mCompositeSubscription;
|
||||
private @NonNull StripeService mStripeService;
|
||||
private @NonNull ProgressListener mProgressListener;
|
||||
|
||||
public ExampleEphemeralKeyProvider(@NonNull ProgressListener progressListener) {
|
||||
Retrofit retrofit = RetrofitFactory.getInstance();
|
||||
mStripeService = retrofit.create(StripeService.class);
|
||||
mCompositeSubscription = new CompositeSubscription();
|
||||
mProgressListener = progressListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createEphemeralKey(@NonNull @Size(min = 4) String apiVersion,
|
||||
@NonNull final EphemeralKeyUpdateListener keyUpdateListener) {
|
||||
Map<String, String> apiParamMap = new HashMap<>();
|
||||
apiParamMap.put("api_version", apiVersion);
|
||||
|
||||
mCompositeSubscription.add(
|
||||
mStripeService.createEphemeralKey(apiParamMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Action1<ResponseBody>() {
|
||||
@Override
|
||||
public void call(ResponseBody response) {
|
||||
try {
|
||||
String rawKey = response.string();
|
||||
keyUpdateListener.onKeyUpdate(rawKey);
|
||||
mProgressListener.onStringResponse(rawKey);
|
||||
} catch (IOException iox) {
|
||||
|
||||
}
|
||||
}
|
||||
}, new Action1<Throwable>() {
|
||||
@Override
|
||||
public void call(Throwable throwable) {
|
||||
mProgressListener.onStringResponse(throwable.getMessage());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public interface ProgressListener {
|
||||
void onStringResponse(String string);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.stripe.example.service;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import com.stripe.android.PaymentConfiguration;
|
||||
import com.stripe.android.Stripe;
|
||||
import com.stripe.android.exception.StripeException;
|
||||
import com.stripe.android.model.Card;
|
||||
import com.stripe.android.model.Token;
|
||||
|
||||
/**
|
||||
* An {@link IntentService} subclass for handling the creation of a {@link Token} from
|
||||
* input {@link Card} information.
|
||||
*/
|
||||
public class TokenIntentService extends IntentService {
|
||||
|
||||
public static final String TOKEN_ACTION = "com.stripe.example.service.tokenAction";
|
||||
public static final String STRIPE_CARD_LAST_FOUR = "com.stripe.example.service.cardLastFour";
|
||||
public static final String STRIPE_CARD_TOKEN_ID = "com.stripe.example.service.cardTokenId";
|
||||
public static final String STRIPE_ERROR_MESSAGE = "com.stripe.example.service.errorMessage";
|
||||
|
||||
private static final String EXTRA_CARD_NUMBER = "com.stripe.example.service.extra.cardNumber";
|
||||
private static final String EXTRA_MONTH = "com.stripe.example.service.extra.month";
|
||||
private static final String EXTRA_YEAR = "com.stripe.example.service.extra.year";
|
||||
private static final String EXTRA_CVC = "com.stripe.example.service.extra.cvc";
|
||||
|
||||
public static Intent createTokenIntent(
|
||||
@NonNull Activity launchingActivity,
|
||||
@Nullable String cardNumber,
|
||||
@Nullable Integer month,
|
||||
@Nullable Integer year,
|
||||
@Nullable String cvc) {
|
||||
return new Intent(launchingActivity, TokenIntentService.class)
|
||||
.putExtra(EXTRA_CARD_NUMBER, cardNumber)
|
||||
.putExtra(EXTRA_MONTH, month)
|
||||
.putExtra(EXTRA_YEAR, year)
|
||||
.putExtra(EXTRA_CVC, cvc);
|
||||
}
|
||||
|
||||
public TokenIntentService() {
|
||||
super("TokenIntentService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
String errorMessage = null;
|
||||
Token token = null;
|
||||
if (intent != null) {
|
||||
final String cardNumber = intent.getStringExtra(EXTRA_CARD_NUMBER);
|
||||
final Integer month = (Integer) intent.getExtras().get(EXTRA_MONTH);
|
||||
final Integer year = (Integer) intent.getExtras().get(EXTRA_YEAR);
|
||||
final String cvc = intent.getStringExtra(EXTRA_CVC);
|
||||
|
||||
final Card card = new Card(cardNumber, month, year, cvc);
|
||||
|
||||
final Stripe stripe = new Stripe(getApplicationContext());
|
||||
try {
|
||||
token = stripe.createTokenSynchronous(card,
|
||||
PaymentConfiguration.getInstance().getPublishableKey());
|
||||
} catch (StripeException stripeEx) {
|
||||
errorMessage = stripeEx.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
final Intent localIntent = new Intent(TOKEN_ACTION);
|
||||
if (token != null) {
|
||||
localIntent.putExtra(STRIPE_CARD_LAST_FOUR, token.getCard().getLast4());
|
||||
localIntent.putExtra(STRIPE_CARD_TOKEN_ID, token.getId());
|
||||
}
|
||||
|
||||
if (errorMessage != null) {
|
||||
localIntent.putExtra(STRIPE_ERROR_MESSAGE, errorMessage);
|
||||
}
|
||||
|
||||
// Broadcasts the Intent to receivers in this app.
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<com.stripe.android.view.CardInputWidget
|
||||
android:id="@+id/card_input_widget"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
Loading…
Reference in New Issue