feat(android): add initial objects

pull/2/merge
codinronan 5 years ago
parent d449073667
commit 80914bdede

@ -7,9 +7,10 @@
<name>Stripe Payments</name>
<description>Cordova plugin for Stripe payments using the native Android/iOS SDKs. Supports Apple Pay and card payments.</description>
<repo>https://github.com/rolamix/cordova-plugin-stripe-payments</repo>
<author>Rolamix, Inc.</author>
<license>MIT</license>
<keywords>cordova,stripe,payments,apple pay,credit cards,checkout</keywords>
<keywords>cordova,stripe,payments,apple pay,ach,google pay,cards,credit cards,checkout</keywords>
<repo>https://github.com/rolamix/cordova-plugin-stripe-payments</repo>
<issue>https://github.com/rolamix/cordova-plugin-stripe-payments/issues</issue>
<engines>
@ -30,6 +31,10 @@
<!-- Android -->
<platform name="android">
<config-file parent="/*/application" target="AndroidManifest.xml">
<meta-data android:name="com.google.android.gms.wallet.api.enabled" android:value="true" />
</config-file>
<config-file target="res/xml/config.xml" parent="/*">
<feature name="StripePaymentsPlugin">
<param name="android-package" value="com.rolamix.plugins.stripe.StripePaymentsPlugin"/>

@ -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…
Cancel
Save