diff --git a/.metadata b/.metadata new file mode 100644 index 00000000..e2ac6f3e --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 2294d75bfa8d067ba90230c0fc2268f3636d7584 + channel: beta + +project_type: plugin diff --git a/android/build.gradle b/android/build.gradle index 6975c127..2365d77d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,8 +9,8 @@ buildscript { } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.70" - classpath 'com.android.tools.build:gradle:3.6.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10" + classpath 'com.android.tools.build:gradle:3.6.3' } } @@ -47,6 +47,11 @@ android { disable 'InvalidPackage' } + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + buildTypes { release { // TODO: Add your own signing config for the release build. @@ -68,8 +73,8 @@ android { } dependencies { - api 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70' + api 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20' implementation 'androidx.annotation:annotation:1.1.0' - implementation 'com.stripe:stripe-android:10.4.6' - implementation 'com.google.android.gms:play-services-wallet:18.0.0' + implementation 'com.stripe:stripe-android:16.1.1' + implementation 'com.google.android.gms:play-services-wallet:18.1.2' } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 3d841775..f43aa779 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="de.jonasbark.stripe_payment"> @@ -9,24 +9,6 @@ android:name="com.google.android.gms.wallet.api.enabled" android:value="true" /> - - - - - - - - - - \ No newline at end of file diff --git a/android/src/main/java/com/facebook/react/bridge/Promise.java b/android/src/main/java/com/facebook/react/bridge/Promise.java index a9bf15aa..014d4d91 100644 --- a/android/src/main/java/com/facebook/react/bridge/Promise.java +++ b/android/src/main/java/com/facebook/react/bridge/Promise.java @@ -1,27 +1,46 @@ package com.facebook.react.bridge; +import android.os.Handler; import io.flutter.plugin.common.MethodChannel; -/** - * Created by FFuF, Jonas Bark on 2019-10-02. - */ +/** Created by FFuF, Jonas Bark on 2019-10-02. */ public class Promise { - final MethodChannel.Result result; + private final MethodChannel.Result result; - public Promise(MethodChannel.Result result) { - this.result = result; - } + private final Handler handler = new Handler(); - public void resolve(Object result) { - this.result.success(result); - } + private Runnable whenComplete; + + public Promise(MethodChannel.Result result) { + this.result = result; + } + + public void setWhenComplete(Runnable whenComplete) { + this.whenComplete = whenComplete; + } - public void reject(String errorCode, String message) { - this.result.error(errorCode, message, null); + public void resolve(Object result) { + this.result.success(result); + + if (whenComplete != null) { + handler.post(whenComplete); } + } + + public void reject(String errorCode) { + reject(errorCode, null, null); + } + + public void reject(String errorCode, String message) { + reject(errorCode, message, null); + } + + public void reject(String errorCode, String message, Object details) { + result.error(errorCode, message, details); - public void reject(String errorCode) { - this.reject(errorCode, null); + if (whenComplete != null) { + handler.post(whenComplete); } + } } diff --git a/android/src/main/java/com/gettipsi/stripe/Errors.java b/android/src/main/java/com/gettipsi/stripe/Errors.java index 6efc7834..d45e0007 100644 --- a/android/src/main/java/com/gettipsi/stripe/Errors.java +++ b/android/src/main/java/com/gettipsi/stripe/Errors.java @@ -1,19 +1,19 @@ package com.gettipsi.stripe; +import android.util.Log; import androidx.annotation.NonNull; - +import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReadableMap; import com.gettipsi.stripe.util.ArgCheck; - +import com.stripe.android.StripeError; +import com.stripe.android.exception.StripeException; import java.util.HashMap; import java.util.Map; +import java.util.Objects; -/** - * Created by ngoriachev on 30/07/2018. - */ - +/** Created by ngoriachev on 30/07/2018. */ public final class Errors { - + private static final String TAG = "Errors"; private static final Map exceptionNameToErrorCode = new HashMap<>(); public static final String CANCELLED = "cancelled"; @@ -32,15 +32,53 @@ public final class Errors { exceptionNameToErrorCode.put("APIException", "api"); } + public static void raiseFlutterError(Exception e, Promise promise) { + if (e instanceof StripeException) { + final StripeException se = (StripeException) e; + final Map details = new HashMap<>(); + + String code = e.getClass().toString(); + if (se.getStripeError() != null) { + details.put("stripeError", decode(se.getStripeError())); + code = se.getStripeError().getCode(); + } + + if (se.getRequestId() != null) { + details.put("requestId", se.getRequestId()); + } + details.put("statusCode", se.getStatusCode()); + + promise.reject(code, e.getMessage(), details); + } + } + private static String toString(Object o, String nullDefault) { + return (o != null) ? o.toString() : nullDefault; + } + + private static Map decode(StripeError error) { + final Map map = new HashMap<>(); + + map.put("type", toString(error.getType(), "")); + map.put("message", toString(error.getMessage(), "")); + map.put("code", toString(error.getCode(), "")); + map.put("param", toString(error.getParam(), "")); + map.put("declineCode", toString(error.getDeclineCode(), "")); + map.put("charge", toString(error.getCharge(), "")); + map.put("docUrl", toString(error.getDocUrl(), "")); + + return map; + } + public static String toErrorCode(@NonNull Exception exception) { ArgCheck.nonNull(exception); String simpleName = exception.getClass().getSimpleName(); + Log.d(TAG, "Simple Error Name: " + simpleName); String errorCode = exceptionNameToErrorCode.get(simpleName); if (errorCode == null) { errorCode = simpleName; } -// ArgCheck.nonNull(errorCode, simpleName); + // ArgCheck.nonNull(errorCode, simpleName); return errorCode; } @@ -52,5 +90,4 @@ static String getErrorCode(@NonNull ReadableMap errorCodes, @NonNull String erro static String getDescription(@NonNull ReadableMap errorCodes, @NonNull String errorKey) { return errorCodes.getMap(errorKey).getString("description"); } - } diff --git a/android/src/main/java/com/gettipsi/stripe/GoogleApiPayFlowImpl.java b/android/src/main/java/com/gettipsi/stripe/GoogleApiPayFlowImpl.java index 0d2dbfac..ff0020da 100644 --- a/android/src/main/java/com/gettipsi/stripe/GoogleApiPayFlowImpl.java +++ b/android/src/main/java/com/gettipsi/stripe/GoogleApiPayFlowImpl.java @@ -1,9 +1,22 @@ package com.gettipsi.stripe; +import static com.gettipsi.stripe.Errors.toErrorCode; +import static com.gettipsi.stripe.util.Converters.convertPaymentMethodToWritableMap; +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.BILLING_ADDRESS_REQUIRED; +import static com.gettipsi.stripe.util.PayParams.CURRENCY_CODE; +import static com.gettipsi.stripe.util.PayParams.EMAIL_REQUIRED; +import static com.gettipsi.stripe.util.PayParams.PHONE_NUMBER_REQUIRED; +import static com.gettipsi.stripe.util.PayParams.SHIPPING_ADDRESS_REQUIRED; +import static com.gettipsi.stripe.util.PayParams.TOTAL_PRICE; + import android.app.Activity; import android.content.Intent; +import android.util.Log; import androidx.annotation.NonNull; - import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReadableMap; import com.gettipsi.stripe.util.ArgCheck; @@ -11,213 +24,242 @@ 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.identity.intents.model.UserAddress; import com.google.android.gms.tasks.Task; import com.google.android.gms.wallet.AutoResolveHelper; +import com.google.android.gms.wallet.CardInfo; 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.PaymentMethodToken; 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.ApiResultCallback; +import com.stripe.android.Stripe; +import com.stripe.android.model.Address; +import com.stripe.android.model.PaymentMethod; +import com.stripe.android.model.PaymentMethodCreateParams; import com.stripe.android.model.Token; - +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; -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 - */ +/** 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 final Fun0 mStripe; private Promise payPromise; + private boolean paymentRequestCreatesPaymentMethod = false; - public GoogleApiPayFlowImpl(@NonNull Fun0 activityProvider) { + public GoogleApiPayFlowImpl(@NonNull Fun0 activityProvider, Fun0 stripeProvider) { super(activityProvider); + + this.mStripe = stripeProvider; } private PaymentsClient createPaymentsClient(@NonNull Activity activity) { return Wallet.getPaymentsClient( - activity, - new Wallet.WalletOptions.Builder().setEnvironment(getEnvironment()).build()); + activity, new Wallet.WalletOptions.Builder().setEnvironment(getEnvironment()).build()); } - private void isReadyToPay(@NonNull Activity activity, boolean isExistingPaymentMethodRequired, @NonNull final Promise promise) { + 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(); + IsReadyToPayRequest.newBuilder() + .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD) + .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD) + .setExistingPaymentMethodRequired(isExistingPaymentMethodRequired) + .build(); mPaymentsClient = createPaymentsClient(activity); Task task = mPaymentsClient.isReadyToPay(request); task.addOnCompleteListener( - new OnCompleteListener() { - public void onComplete(Task task) { + task1 -> { try { - boolean result = task.getResult(ApiException.class); + Boolean result = task1.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", BuildConfig.VERSION_NAME) - .build(); + .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 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 allowedCountryCodes = getAllowedShippingCountryCodes(payParams); return createPaymentDataRequest( - estimatedTotalPrice, - currencyCode, - billingAddressRequired, - shippingAddressRequired, - phoneNumberRequired, - emailRequired, - allowedCountryCodes - ); + 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 countryCodes - ) { + 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 countryCodes) { ArgCheck.isDouble(totalPrice); ArgCheck.notEmptyString(currencyCode); + Log.d(TAG, "email required: " + emailRequired); + PaymentDataRequest.Builder builder = PaymentDataRequest.newBuilder(); builder.setTransactionInfo( - TransactionInfo.newBuilder() - .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_ESTIMATED) - .setTotalPrice(totalPrice) - .setCurrencyCode(currencyCode) - .build()); + 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); + .setCardRequirements( + CardRequirements.newBuilder() + .addAllowedCardNetworks( + Arrays.asList( + WalletConstants.CARD_NETWORK_AMEX, + WalletConstants.CARD_NETWORK_DISCOVER, + WalletConstants.CARD_NETWORK_JCB, + WalletConstants.CARD_NETWORK_MASTERCARD, + WalletConstants.CARD_NETWORK_VISA, + WalletConstants.CARD_NETWORK_INTERAC, + WalletConstants.CARD_NETWORK_OTHER)) + .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()); + ShippingAddressRequirements.newBuilder().addAllowedCountryCodes(countryCodes).build()); } builder.setPaymentMethodTokenizationParameters(createPaymentMethodTokenizationParameters()); return builder.build(); } - private void startPaymentRequest(@NonNull Activity activity, @NonNull PaymentDataRequest request) { + 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); + mPaymentsClient.loadPaymentData(request), activity, LOAD_PAYMENT_DATA_REQUEST_CODE); + } + + @Override + public void paymentMethodFromAndroidPay( + @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; + } + + paymentRequestCreatesPaymentMethod = true; + + this.payPromise = promise; + startPaymentRequest(activity, createPaymentDataRequest(payParams)); } @Override - public void paymentRequestWithAndroidPay(@NonNull ReadableMap payParams, @NonNull Promise promise) { + 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") - ); + getErrorCode("activityUnavailable"), getErrorDescription("activityUnavailable")); return; } + paymentRequestCreatesPaymentMethod = false; + this.payPromise = promise; startPaymentRequest(activity, createPaymentDataRequest(payParams)); } @Override - public void deviceSupportsAndroidPay(boolean isExistingPaymentMethodRequired, @NonNull Promise promise) { + public void deviceSupportsAndroidPay( + boolean isExistingPaymentMethodRequired, @NonNull Promise promise) { Activity activity = activityProvider.call(); if (activity == null) { promise.reject( - getErrorCode("activityUnavailable"), - getErrorDescription("activityUnavailable") - ); + getErrorCode("activityUnavailable"), getErrorDescription("activityUnavailable")); return; } if (!isPlayServicesAvailable(activity)) { promise.reject( - getErrorCode("playServicesUnavailable"), - getErrorDescription("playServicesUnavailable") - ); + getErrorCode("playServicesUnavailable"), getErrorDescription("playServicesUnavailable")); return; } isReadyToPay(activity, isExistingPaymentMethodRequired, promise); } + public void potentiallyAvailableNativePayNetworks(@NonNull Promise promise) { + List networks = new ArrayList<>(); + networks.add("visa"); + networks.add("jcb"); + networks.add("master"); + promise.resolve(networks); + } + public boolean onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { if (payPromise == null) { return false; @@ -229,36 +271,116 @@ public boolean onActivityResult(Activity activity, int requestCode, int resultCo case Activity.RESULT_OK: PaymentData paymentData = PaymentData.getFromIntent(data); ArgCheck.nonNull(paymentData); - String tokenJson = paymentData.getPaymentMethodToken().getToken(); - Token token = Token.fromString(tokenJson); - if (token == null) { - payPromise.reject( - getErrorCode("parseResponse"), - getErrorDescription("parseResponse") - ); + + if (paymentRequestCreatesPaymentMethod) { + try { + final CardInfo info = paymentData.getCardInfo(); + // You can also pull the user address from the PaymentData object. + final UserAddress address = paymentData.getShippingAddress(); + final PaymentMethodToken paymentMethodToken = paymentData.getPaymentMethodToken(); + // This is the raw string version of your Stripe token. + final String rawToken = + paymentMethodToken != null ? paymentMethodToken.getToken() : null; + + final Token stripeToken = Token.fromJson(new JSONObject(rawToken)); + if (stripeToken != null) { + // Create a PaymentMethod object using the token id + final PaymentMethod.BillingDetails billingDetails; + if (address != null) { + billingDetails = + new PaymentMethod.BillingDetails.Builder() + .setAddress( + new Address.Builder() + .setLine1(address.getAddress1()) + .setLine2(address.getAddress2()) + .setCity(address.getLocality()) + .setState(address.getAdministrativeArea()) + .setPostalCode(address.getPostalCode()) + .setCountry(address.getCountryCode()) + .build()) + // TODO: Should fix this. And revert to the below implementation. + // .setEmail(address.getEmailAddress()) + .setEmail(paymentData.getEmail()) + .setName(address.getName()) + .setPhone(address.getPhoneNumber()) + .build(); + } else { + billingDetails = + new PaymentMethod.BillingDetails.Builder() + .setEmail(paymentData.getEmail()) + .build(); + } + final PaymentMethodCreateParams params = + PaymentMethodCreateParams.create( + PaymentMethodCreateParams.Card.create(stripeToken.getId()), + billingDetails); + + mStripe.call().createPaymentMethod( + params, + new ApiResultCallback() { + @Override + public void onSuccess(PaymentMethod paymentMethod) { + Log.d(TAG, "paymentMethod: " + paymentMethod.toString()); + payPromise.resolve(convertPaymentMethodToWritableMap(paymentMethod)); + payPromise = null; + } + + @Override + public void onError(@NotNull Exception e) { + payPromise.reject( + getErrorCode("parseResponse"), getErrorDescription("parseResponse")); + payPromise = null; + } + }); + return true; + } else { + payPromise.reject( + getErrorCode("parseResponse"), getErrorDescription("parseResponse")); + } + + } catch (Exception e) { + Log.e(TAG, "Error creating payment method", e); + payPromise.reject( + getErrorCode("parseResponse"), getErrorDescription("parseResponse")); + } } else { - payPromise.resolve(putExtraToTokenMap( - convertTokenToWritableMap(token), - getBillingAddress(paymentData), - paymentData.getShippingAddress(), - paymentData.getEmail())); + String tokenJson = paymentData.getPaymentMethodToken().getToken(); + Token token = null; + try { + token = Token.fromJson(new JSONObject(tokenJson)); + } catch (JSONException e) { + payPromise.resolve( + putExtraToTokenMap( + convertTokenToWritableMap(token), + getBillingAddress(paymentData), + paymentData.getShippingAddress(), + paymentData.getEmail())); + break; + } + Log.d(TAG, "token: " + tokenJson); + 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") - ); + 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() - ); + payPromise.reject(getErrorCode("stripe"), status.getStatusMessage()); break; default: @@ -270,5 +392,4 @@ public boolean onActivityResult(Activity activity, int requestCode, int resultCo return false; } - } diff --git a/android/src/main/java/com/gettipsi/stripe/OpenBrowserActivity.java b/android/src/main/java/com/gettipsi/stripe/OpenBrowserActivity.java deleted file mode 100644 index 5ae1051c..00000000 --- a/android/src/main/java/com/gettipsi/stripe/OpenBrowserActivity.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gettipsi.stripe; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import androidx.annotation.Nullable; - -/** - * Created by remer on 16/11/17. - */ -public class OpenBrowserActivity extends Activity { - final static String EXTRA_URL = "url"; - - private String url; - private boolean shouldFinish = true; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - shouldFinish = false; - - url = getIntent().getStringExtra(EXTRA_URL); - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)) - .addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_TOP | - Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(browserIntent); - } - - @Override - protected void onResume() { - super.onResume(); - if (shouldFinish) { - StripeModule.getInstance().processRedirect(null); - finish(); - } - shouldFinish = true; - } -} diff --git a/android/src/main/java/com/gettipsi/stripe/PayFlow.java b/android/src/main/java/com/gettipsi/stripe/PayFlow.java index fa3160ff..d000c85b 100644 --- a/android/src/main/java/com/gettipsi/stripe/PayFlow.java +++ b/android/src/main/java/com/gettipsi/stripe/PayFlow.java @@ -2,9 +2,8 @@ import android.app.Activity; import android.content.Intent; -import androidx.annotation.NonNull; import android.util.Log; - +import androidx.annotation.NonNull; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReadableMap; import com.gettipsi.stripe.util.ArgCheck; @@ -13,6 +12,7 @@ import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.wallet.WalletConstants; import com.stripe.android.BuildConfig; +import com.stripe.android.Stripe; public abstract class PayFlow { @@ -26,18 +26,19 @@ public PayFlow(@NonNull Fun0 activityProvider) { this.activityProvider = activityProvider; } - public static PayFlow create(Fun0 activityProvider) { - return new GoogleApiPayFlowImpl(activityProvider); + public static PayFlow create(Fun0 activityProvider, Fun0 stripeProvider) { + return new GoogleApiPayFlowImpl(activityProvider, stripeProvider); } private static boolean isValidEnvironment(int environment) { - return environment == WalletConstants.ENVIRONMENT_TEST || - environment == WalletConstants.ENVIRONMENT_PRODUCTION; + return environment == WalletConstants.ENVIRONMENT_TEST + || environment == WalletConstants.ENVIRONMENT_PRODUCTION; } private static boolean isEnvironmentChangeAttempt(int oldEnvironment, int newEnvironment) { - return oldEnvironment != newEnvironment && isValidEnvironment(oldEnvironment) && - isValidEnvironment(newEnvironment); + return oldEnvironment != newEnvironment + && isValidEnvironment(oldEnvironment) + && isValidEnvironment(newEnvironment); } protected int getEnvironment() { @@ -79,11 +80,17 @@ protected String getErrorDescription(String key) { return Errors.getDescription(getErrorCodes(), key); } + abstract void paymentMethodFromAndroidPay(final ReadableMap payParams, final Promise promise); + abstract void paymentRequestWithAndroidPay(final ReadableMap payParams, final Promise promise); - abstract void deviceSupportsAndroidPay(boolean isExistingPaymentMethodRequired, final Promise promise); + abstract void deviceSupportsAndroidPay( + boolean isExistingPaymentMethodRequired, final Promise promise); + + abstract void potentiallyAvailableNativePayNetworks(final Promise promise); - abstract boolean onActivityResult(Activity activity, int requestCode, int resultCode, Intent data); + abstract boolean onActivityResult( + Activity activity, int requestCode, int resultCode, Intent data); public static boolean isPlayServicesAvailable(@NonNull Activity activity) { ArgCheck.nonNull(activity); diff --git a/android/src/main/java/com/gettipsi/stripe/RedirectUriReceiver.java b/android/src/main/java/com/gettipsi/stripe/RedirectUriReceiver.java deleted file mode 100644 index 37f589a9..00000000 --- a/android/src/main/java/com/gettipsi/stripe/RedirectUriReceiver.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gettipsi.stripe; - -import android.app.Activity; -import android.os.Bundle; -import androidx.annotation.Nullable; -import android.util.Log; - -/** - * Created by remer on 11/8/17. - */ - -public class RedirectUriReceiver extends Activity { - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (StripeModule.getInstance() == null) { - sendResult(RESULT_CANCELED); - } - - StripeModule.getInstance().processRedirect(getIntent().getData()); - sendResult(RESULT_OK); - } - - private void sendResult(int resultCode) { - setResult(resultCode); - finish(); - } -} diff --git a/android/src/main/java/com/gettipsi/stripe/StripeModule.java b/android/src/main/java/com/gettipsi/stripe/StripeModule.java index 77f58bdc..db23596c 100644 --- a/android/src/main/java/com/gettipsi/stripe/StripeModule.java +++ b/android/src/main/java/com/gettipsi/stripe/StripeModule.java @@ -1,85 +1,123 @@ package com.gettipsi.stripe; +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.createBankAccountTokenParams; +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; + import android.app.Activity; -import android.content.DialogInterface; -import android.content.Intent; +import android.content.Context; import android.net.Uri; import android.os.AsyncTask; import android.text.TextUtils; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.facebook.react.bridge.*; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactMethod; +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.wallet.WalletConstants; -import com.stripe.android.*; -import com.stripe.android.model.*; - -import de.jonasbark.stripepayment.StripeDialog; -import io.flutter.app.FlutterActivity; +import com.stripe.android.ApiResultCallback; +import com.stripe.android.AppInfo; +import com.stripe.android.PaymentIntentResult; +import com.stripe.android.SetupIntentResult; +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.Redirect; +import com.stripe.android.model.SourceParams; +import com.stripe.android.model.StripeIntent; +import com.stripe.android.model.Token; +import de.jonasbark.stripe_payment.ActivityRegistry; +import de.jonasbark.stripe_payment.StripeDialog; import io.flutter.plugin.common.PluginRegistry; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; - +import io.flutter.plugin.common.PluginRegistry.ActivityResultListener; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import kotlin.Unit; +import org.jetbrains.annotations.NotNull; -import static com.gettipsi.stripe.Errors.*; -import static com.gettipsi.stripe.util.Converters.*; -import static com.gettipsi.stripe.util.InitializationOptions.*; -import static com.stripe.android.model.StripeIntent.Status.*; - -public class StripeModule extends ReactContextBaseJavaModule { - +public class StripeModule { - private static final String MODULE_NAME = StripeModule.class.getSimpleName(); + private static final String TAG = "StripeModule"; // 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.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_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; + @NonNull private Context applicationContext; - public static StripeModule getInstance() { - return sInstance; - } + @Nullable public Activity activity = null; + + private final ActivityRegistry activityRegistry; public Stripe getStripe() { return mStripe; } - @Nullable - private Promise mCreateSourcePromise; + @Nullable private Promise mCreateSourcePromise; - @Nullable - private Source mCreatedSource; + @Nullable private Source mCreatedSource; private String mPublicKey; private Stripe mStripe; private PayFlow mPayFlow; private ReadableMap mErrorCodes; - private final PluginRegistry.ActivityResultListener mActivityEventListener = new PluginRegistry.ActivityResultListener() { - - @Override - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - boolean handled = getPayFlow().onActivityResult(activity, requestCode, resultCode, data); - return handled; - } - }; - - public StripeModule(PluginRegistry.Registrar registrar, Activity activity) { - super(activity, registrar); - registrar.addActivityResultListener(mActivityEventListener); - - sInstance = this; + private final PluginRegistry.ActivityResultListener activityResultListener = + (requestCode, resultCode, data) -> { + if (activity != null) { + return getPayFlow().onActivityResult(activity, requestCode, resultCode, data); + } + return false; + }; + + public StripeModule( + @NotNull Context applicationContext, + @Nullable Activity activity, + ActivityRegistry activityRegistry) { + this.applicationContext = applicationContext; + this.activity = activity; + this.activityRegistry = activityRegistry; + + activityRegistry.addListener(activityResultListener); } @ReactMethod @@ -94,12 +132,15 @@ public void init(@NonNull ReadableMap options, @NonNull ReadableMap errorCodes) mPublicKey = newPubKey; Stripe.setAppInfo(AppInfo.create(APP_INFO_NAME, APP_INFO_VERSION, APP_INFO_URL)); - mStripe = new Stripe(getReactApplicationContext(), mPublicKey); + mStripe = new Stripe(applicationContext, mPublicKey); + getPayFlow().setPublishableKey(mPublicKey); } if (newAndroidPayMode != null) { - ArgCheck.isTrue(ANDROID_PAY_MODE_TEST.equals(newAndroidPayMode) || ANDROID_PAY_MODE_PRODUCTION.equals(newAndroidPayMode)); + ArgCheck.isTrue( + ANDROID_PAY_MODE_TEST.equals(newAndroidPayMode) + || ANDROID_PAY_MODE_PRODUCTION.equals(newAndroidPayMode)); getPayFlow().setEnvironment(androidPayModeToEnvironment(newAndroidPayMode)); } @@ -112,11 +153,7 @@ public void init(@NonNull ReadableMap options, @NonNull ReadableMap errorCodes) private PayFlow getPayFlow() { if (mPayFlow == null) { - mPayFlow = PayFlow.create( - new Fun0() { public Activity call() { - return getCurrentActivity(); - }} - ); + mPayFlow = PayFlow.create(() -> activity, () -> mStripe); } return mPayFlow; @@ -124,7 +161,9 @@ private PayFlow getPayFlow() { private static int androidPayModeToEnvironment(@NonNull String androidPayMode) { ArgCheck.notEmptyString(androidPayMode); - return ANDROID_PAY_MODE_TEST.equals(androidPayMode.toLowerCase()) ? WalletConstants.ENVIRONMENT_TEST : WalletConstants.ENVIRONMENT_PRODUCTION; + return ANDROID_PAY_MODE_TEST.equals(androidPayMode.toLowerCase()) + ? WalletConstants.ENVIRONMENT_TEST + : WalletConstants.ENVIRONMENT_PRODUCTION; } @ReactMethod @@ -137,13 +176,18 @@ public void canMakeAndroidPayPayments(final Promise promise) { getPayFlow().deviceSupportsAndroidPay(true, promise); } + @ReactMethod + public void potentiallyAvailableNativePayNetworks(final Promise promise) { + getPayFlow().potentiallyAvailableNativePayNetworks(promise); + } + @ReactMethod public void setStripeAccount(final String stripeAccount) { ArgCheck.notEmptyString(mPublicKey); if (stripeAccount == null) { - mStripe = new Stripe(getReactApplicationContext(), mPublicKey); + mStripe = new Stripe(applicationContext, mPublicKey); } else { - mStripe = new Stripe(getReactApplicationContext(), mPublicKey, stripeAccount); + mStripe = new Stripe(applicationContext, mPublicKey, stripeAccount); } } @@ -153,18 +197,21 @@ public void createTokenWithCard(final ReadableMap cardData, final Promise promis ArgCheck.nonNull(mStripe); ArgCheck.notEmptyString(mPublicKey); - mStripe.createToken( - createCard(cardData), - mPublicKey, - new TokenCallback() { - public void onSuccess(Token token) { - promise.resolve(convertTokenToWritableMap(token)); - } - public void onError(Exception error) { - error.printStackTrace(); - promise.reject(toErrorCode(error), error.getMessage()); - } - }); + mStripe.createCardToken( + createCard(cardData), + null, + new ApiResultCallback() { + @Override + public void onSuccess(Token token) { + promise.resolve(convertTokenToWritableMap(token)); + } + + @Override + public void onError(@NotNull Exception error) { + error.printStackTrace(); + promise.reject(toErrorCode(error), error.getMessage()); + } + }); } catch (Exception e) { promise.reject(toErrorCode(e), e.getMessage()); } @@ -177,18 +224,18 @@ public void createTokenWithBankAccount(final ReadableMap accountData, final Prom ArgCheck.notEmptyString(mPublicKey); mStripe.createBankAccountToken( - createBankAccount(accountData), - mPublicKey, - null, - new TokenCallback() { - public void onSuccess(Token token) { - promise.resolve(convertTokenToWritableMap(token)); - } - public void onError(Exception error) { - error.printStackTrace(); - promise.reject(toErrorCode(error), error.getMessage()); - } - }); + createBankAccountTokenParams(accountData), + null, + new ApiResultCallback() { + public void onSuccess(Token token) { + promise.resolve(convertTokenToWritableMap(token)); + } + + public void onError(@NonNull Exception error) { + error.printStackTrace(); + promise.reject(toErrorCode(error), error.getMessage()); + } + }); } catch (Exception e) { promise.reject(toErrorCode(e), e.getMessage()); } @@ -196,140 +243,134 @@ public void onError(Exception error) { @ReactMethod public void paymentRequestWithCardForm(ReadableMap params, final Promise promise) { - Activity currentActivity = getCurrentActivity(); + Activity currentActivity = activity; try { ArgCheck.nonNull(currentActivity); ArgCheck.notEmptyString(mPublicKey); - final StripeDialog cardDialog = StripeDialog.newInstance( - "" - ); + final StripeDialog cardDialog = StripeDialog.newInstance(""); cardDialog.setStripeInstance(mStripe); - cardDialog.setTokenListener(new Function1() { - @Override - public Unit invoke(PaymentMethod paymentMethod) { - promise.resolve(Converters.convertPaymentMethodToWritableMap(paymentMethod)); - return Unit.INSTANCE; - } - }); - cardDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - promise.reject(CANCELLED, CANCELLED); - } - }); + cardDialog.setTokenListener( + paymentMethod -> { + promise.resolve(Converters.convertPaymentMethodToWritableMap(paymentMethod)); + return Unit.INSTANCE; + }); + cardDialog.setOnCancelListener(dialog -> promise.reject(CANCELLED, CANCELLED)); cardDialog.show(currentActivity.getFragmentManager(), "AddNewCard"); } catch (Exception e) { promise.reject(toErrorCode(e), e.getMessage()); } } + @ReactMethod + public void paymentMethodFromAndroidPay(final ReadableMap payParams, final Promise promise) { + getPayFlow().paymentMethodFromAndroidPay(payParams, promise); + } + @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() { - @Override - public void onSuccess(@NonNull PaymentIntentResult result) { - 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) { - removeActivityEventListener(ael); - e.printStackTrace(); - promise.reject(toErrorCode(e), e.getMessage()); + final ActivityResultListener ael = + (requestCode, resultCode, data) -> + mStripe.onPaymentResult( + requestCode, + data, + new ApiResultCallback() { + @Override + public void onSuccess(@NonNull PaymentIntentResult result) { + 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) { + e.printStackTrace(); + promise.reject(toErrorCode(e), e.getMessage()); + } + }); + + activityRegistry.addListener(ael); + + promise.setWhenComplete( + () -> { + if (activityRegistry.removeListener(ael)) { + hasSetupResultListener.set(false); } }); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - onActivityResult(null, requestCode, resultCode, data); - } - }; - 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; + private AtomicBoolean hasSetupResultListener = new AtomicBoolean(false); - mStripe.onSetupResult(requestCode, data, new ApiResultCallback() { - @Override - public void onSuccess(@NonNull SetupIntentResult result) { - 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"); - } - } + private void attachSetupResultActivityListener(final Promise promise) { + ActivityResultListener ael = + (requestCode, resultCode, data) -> + mStripe.onSetupResult( + requestCode, + data, + new ApiResultCallback() { + @Override + public void onSuccess(@NonNull SetupIntentResult result) { + 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) { + Log.e(TAG, "Unexpected error", e); + promise.reject(UNEXPECTED, "Unexpected error"); + } + } + + @Override + public void onError(@NonNull Exception e) { + Errors.raiseFlutterError(e, promise); + } + }); + + if (!hasSetupResultListener.getAndSet(true)) { + activityRegistry.addListener(ael); + } - @Override - public void onError(@NonNull Exception e) { - removeActivityEventListener(ael); - e.printStackTrace(); - promise.reject(toErrorCode(e), e.getMessage()); + promise.setWhenComplete( + () -> { + if (activityRegistry.removeListener(ael)) { + hasSetupResultListener.set(false); } }); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - onActivityResult(null, requestCode, resultCode, data); - } - }; - 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)); } @@ -340,7 +381,6 @@ public void authenticatePaymentIntent(final ReadableMap options, final Promise p attachPaymentResultActivityListener(promise); String clientSecret = options.getString(CLIENT_SECRET); - Activity activity = getCurrentActivity(); if (activity != null) { mStripe.authenticatePayment(activity, clientSecret); } @@ -350,7 +390,6 @@ public void authenticatePaymentIntent(final ReadableMap options, final Promise p public void confirmSetupIntent(final ReadableMap options, final Promise promise) { attachSetupResultActivityListener(promise); - Activity activity = getCurrentActivity(); if (activity != null) { mStripe.confirmSetupIntent(activity, extractConfirmSetupIntentParams(options)); } @@ -361,69 +400,69 @@ public void authenticateSetupIntent(final ReadableMap options, final Promise pro 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() { + mStripe.createPaymentMethod( + pmcp, + new ApiResultCallback() { - @Override - public void onError(Exception error) { - promise.reject(toErrorCode(error), error.getMessage()); - } + @Override + public void onError(@NonNull Exception error) { + promise.reject(toErrorCode(error), error.getMessage()); + } - @Override - public void onSuccess(PaymentMethod paymentMethod) { - promise.resolve(convertPaymentMethodToWritableMap(paymentMethod)); - } - }); + @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 SourceCallback() { - @Override - public void onError(Exception error) { - promise.reject(toErrorCode(error)); - } + mStripe.createSource( + sourceParams, + new ApiResultCallback() { + @Override + public void onError(@NonNull Exception error) { + promise.reject(toErrorCode(error)); + } - @Override - public void onSuccess(Source source) { - if (Source.SourceFlow.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); + @Override + public void onSuccess(Source source) { + if (source.getRedirect() != null) { + if (activity == null) { + promise.reject( + getErrorCode(mErrorCodes, "activityUnavailable"), + getDescription(mErrorCodes, "activityUnavailable")); + } else { + mCreateSourcePromise = promise; + mCreatedSource = source; + // String redirectUrl = source.getRedirect().getUrl(); + // Intent browserIntent = new Intent(activity, OpenBrowserActivity.class) + // .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | + // Intent.FLAG_ACTIVITY_SINGLE_TOP) + // .putExtra(OpenBrowserActivity.EXTRA_URL, redirectUrl); + // activity.startActivity(browserIntent); + // TODO + } + } else { + promise.resolve(convertSourceToWritableMap(source)); + } } - } else { - promise.resolve(convertSourceToWritableMap(source)); - } - } - }); + }); } private ConfirmSetupIntentParams extractConfirmSetupIntentParams(final ReadableMap options) { @@ -432,13 +471,14 @@ private ConfirmSetupIntentParams extractConfirmSetupIntentParams(final ReadableM String returnURL = getStringOrNull(options, "returnURL"); String clientSecret = options.getString("clientSecret"); ConfirmSetupIntentParams csip = null; - if (returnURL == null) { + if (returnURL == null) { returnURL = "stripejs://use_stripe_sdk/return_url"; } if (paymentMethod != null) { - csip = ConfirmSetupIntentParams.create(extractPaymentMethodCreateParams(paymentMethod), - clientSecret, returnURL); + csip = + ConfirmSetupIntentParams.create( + extractPaymentMethodCreateParams(paymentMethod), clientSecret, returnURL); } else if (paymentMethodId != null) { csip = ConfirmSetupIntentParams.create(paymentMethodId, clientSecret, returnURL); } @@ -453,13 +493,13 @@ private ConfirmPaymentIntentParams extractConfirmPaymentIntentParams(final Reada String clientSecret = options.getString("clientSecret"); ReadableMap paymentMethod = getMapOrNull(options, "paymentMethod"); - String paymentMethodId = getStringOrNull(options,"paymentMethodId"); + String paymentMethodId = getStringOrNull(options, "paymentMethodId"); // ReadableMap source = options.getMap("source"); // String sourceId = getStringOrNull(options,"sourceId"); String returnURL = getStringOrNull(options, "returnURL"); - if (returnURL == null) { + if (returnURL == null) { returnURL = "stripejs://use_stripe_sdk/return_url"; } boolean savePaymentMethod = getBooleanOrNull(options, "savePaymentMethod", false); @@ -471,41 +511,45 @@ private ConfirmPaymentIntentParams extractConfirmPaymentIntentParams(final Reada if (paymentMethod != null) { PaymentMethodCreateParams pmcp = extractPaymentMethodCreateParams(paymentMethod); - cpip = ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams(pmcp, clientSecret, returnURL, savePaymentMethod, extraParams); + cpip = + ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams( + pmcp, clientSecret, returnURL, savePaymentMethod, extraParams); - // Create with Payment Method ID + // 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. - */ + 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); } @@ -537,22 +581,24 @@ private PaymentMethodCreateParams extractPaymentMethodCreateParams(final Readabl ReadableMap addressParams = getMapOrNull(options, "address"); if (addressParams != null) { - address = new Address.Builder(). - setCity(getStringOrNull(addressParams, "city")). - setCountry(addressParams.getString("country")). - setLine1(getStringOrNull(addressParams, "line1")). - setLine2(getStringOrNull(addressParams, "line2")). - setPostalCode(getStringOrNull(addressParams, "postalCode")). - setState(getStringOrNull(addressParams, "state")). - build(); + address = + new Address.Builder() + .setCity(getStringOrNull(addressParams, "city")) + .setCountry(addressParams.getString("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(); + billingDetails = + new PaymentMethod.BillingDetails.Builder() + .setAddress(address) + .setEmail(getStringOrNull(billingDetailsParams, "email")) + .setName(getStringOrNull(billingDetailsParams, "name")) + .setPhone(getStringOrNull(billingDetailsParams, "phone")) + .build(); } if (cardParams != null) { @@ -560,20 +606,17 @@ private PaymentMethodCreateParams extractPaymentMethodCreateParams(final Readabl 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(); + 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 - ); + return PaymentMethodCreateParams.create(card, billingDetails, metadata); } private SourceParams extractSourceParams(final ReadableMap options) { @@ -581,58 +624,65 @@ private SourceParams extractSourceParams(final ReadableMap options) { 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")); + 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")); + 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")); + 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")); + 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")); + 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")); + 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")); + sourceParams = + SourceParams.createThreeDSecureParams( + options.getInt("amount"), + options.getString("currency"), + options.getString("returnURL"), + options.getString("card")); break; case "card": sourceParams = SourceParams.createCardParams(Converters.createCard(options)); @@ -641,7 +691,6 @@ private SourceParams extractSourceParams(final ReadableMap options) { return sourceParams; } - void processRedirect(@Nullable Uri redirectData) { if (mCreatedSource == null || mCreateSourcePromise == null) { @@ -649,11 +698,9 @@ void processRedirect(@Nullable Uri redirectData) { } if (redirectData == null) { - mCreateSourcePromise.reject( - getErrorCode(mErrorCodes, "redirectCancelled"), - getDescription(mErrorCodes, "redirectCancelled") - ); + getErrorCode(mErrorCodes, "redirectCancelled"), + getDescription(mErrorCodes, "redirectCancelled")); mCreatedSource = null; mCreateSourcePromise = null; return; @@ -662,9 +709,8 @@ void processRedirect(@Nullable Uri redirectData) { final String clientSecret = redirectData.getQueryParameter("client_secret"); if (!mCreatedSource.getClientSecret().equals(clientSecret)) { mCreateSourcePromise.reject( - getErrorCode(mErrorCodes, "redirectNoSource"), - getDescription(mErrorCodes, "redirectNoSource") - ); + getErrorCode(mErrorCodes, "redirectNoSource"), + getDescription(mErrorCodes, "redirectNoSource")); mCreatedSource = null; mCreateSourcePromise = null; return; @@ -673,9 +719,8 @@ void processRedirect(@Nullable Uri redirectData) { final String sourceId = redirectData.getQueryParameter("source"); if (!mCreatedSource.getId().equals(sourceId)) { mCreateSourcePromise.reject( - getErrorCode(mErrorCodes, "redirectWrongSourceId"), - getDescription(mErrorCodes, "redirectWrongSourceId") - ); + getErrorCode(mErrorCodes, "redirectWrongSourceId"), + getDescription(mErrorCodes, "redirectWrongSourceId")); mCreatedSource = null; mCreateSourcePromise = null; return; @@ -701,27 +746,24 @@ protected Source doInBackground(Void... voids) { protected void onPostExecute(Source source) { if (source != null) { switch (source.getStatus()) { - case Source.SourceStatus.CHARGEABLE: - case Source.SourceStatus.CONSUMED: + case Chargeable: + case Consumed: promise.resolve(convertSourceToWritableMap(source)); break; - case Source.SourceStatus.CANCELED: + case Canceled: promise.reject( - getErrorCode(mErrorCodes, "redirectCancelled"), - getDescription(mErrorCodes, "redirectCancelled") - ); + getErrorCode(mErrorCodes, "redirectCancelled"), + getDescription(mErrorCodes, "redirectCancelled")); break; - case Source.SourceStatus.PENDING: - case Source.SourceStatus.FAILED: - default: + case Failed: + case Pending: promise.reject( - getErrorCode(mErrorCodes, "redirectFailed"), - getDescription(mErrorCodes, "redirectFailed") - ); + getErrorCode(mErrorCodes, "redirectFailed"), + getDescription(mErrorCodes, "redirectFailed")); + break; } } } }.execute(); } - } diff --git a/android/src/main/java/com/gettipsi/stripe/util/Converters.java b/android/src/main/java/com/gettipsi/stripe/util/Converters.java index fb2572da..59d79ddb 100644 --- a/android/src/main/java/com/gettipsi/stripe/util/Converters.java +++ b/android/src/main/java/com/gettipsi/stripe/util/Converters.java @@ -1,9 +1,8 @@ package com.gettipsi.stripe.util; +import android.text.TextUtils; 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; @@ -17,31 +16,34 @@ 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.BankAccountTokenParams.Type; import com.stripe.android.model.Card; +import com.stripe.android.model.CardBrand; +import com.stripe.android.model.CardFunding; 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.SourceCodeVerification; -import com.stripe.android.model.SourceOwner; -import com.stripe.android.model.SourceReceiver; -import com.stripe.android.model.SourceRedirect; +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.Locale; import java.util.Map; -/** - * Created by ngoriachev on 13/03/2018. - */ - +/** 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; + if (token == null) { + return newToken; + } newToken.putString("tokenId", token.getId()); newToken.putBoolean("livemode", token.getLivemode()); @@ -58,19 +60,22 @@ public static WritableMap convertTokenToWritableMap(Token token) { return newToken; } - public static WritableMap putExtraToTokenMap(final WritableMap tokenMap, UserAddress billingAddress, UserAddress shippingAddress, String emailAddress) { + 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 + // 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); @@ -82,26 +87,28 @@ public static WritableMap putExtraToTokenMap(final WritableMap tokenMap, UserAdd private static WritableMap convertCardToWritableMap(final Card card) { WritableMap result = Arguments.createMap(); - if (card == null) return result; + 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("brand", card.getBrand() ); - result.putString("funding", card.getFunding() ); - result.putString("fingerprint", card.getFingerprint() ); - result.putString("country", card.getCountry() ); - result.putString("currency", card.getCurrency() ); + 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("brand", card.getBrand().name()); + result.putString("funding", card.getFunding().name()); + result.putString("fingerprint", card.getFingerprint()); + result.putString("country", card.getCountry()); + result.putString("currency", card.getCurrency()); return result; } @@ -109,14 +116,15 @@ private static WritableMap convertCardToWritableMap(final Card card) { public static WritableMap convertBankAccountToWritableMap(BankAccount account) { WritableMap result = Arguments.createMap(); - if (account == null) return result; + if (account == null) { + return result; + } result.putString("routingNumber", account.getRoutingNumber()); - result.putString("accountNumber", account.getAccountNumber()); result.putString("countryCode", account.getCountryCode()); result.putString("currency", account.getCurrency()); result.putString("accountHolderName", account.getAccountHolderName()); - result.putString("accountHolderType", account.getAccountHolderType()); + result.putString("accountHolderType", enumName(account.getAccountHolderType())); result.putString("fingerprint", account.getFingerprint()); result.putString("bankName", account.getBankName()); result.putString("last4", account.getLast4()); @@ -142,7 +150,8 @@ public static Boolean getValue(final ReadableMap map, final String key, final Bo } } - public static ReadableArray getValue(final ReadableMap map, final String key, final ReadableArray def) { + public static ReadableArray getValue( + final ReadableMap map, final String key, final ReadableArray def) { if (map.hasKey(key)) { return map.getArray(key); } else { @@ -159,7 +168,7 @@ public static Collection getAllowedShippingCountryCodes(final ReadableMa ArrayList allowedCountryCodesForShipping = new ArrayList<>(); ReadableArray countries = getValue(map, "shipping_countries", (ReadableArray) null); - if (countries != null){ + if (countries != null) { for (int i = 0; i < countries.size(); i++) { String code = countries.getString(i); allowedCountryCodesForShipping.add(code); @@ -173,7 +182,7 @@ public static ArrayList getAllowedShippingCountries(final ArrayList allowedCountriesForShipping = new ArrayList<>(); ReadableArray countries = getValue(map, "shipping_countries", (ReadableArray) null); - if (countries != null){ + if (countries != null) { for (int i = 0; i < countries.size(); i++) { String code = countries.getString(i); allowedCountriesForShipping.add(new CountrySpecification(code)); @@ -185,28 +194,35 @@ public static ArrayList getAllowedShippingCountries(final 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")) - .brand(getValue(cardData, "brand")) - .last4(getValue(cardData, "last4")) - .fingerprint(getValue(cardData, "fingerprint")) - .funding(getValue(cardData, "funding")) - .country(getValue(cardData, "country")) - .currency(getValue(cardData, "currency")) - .id(getValue(cardData, "id")) - .build(); - } - + 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")) + .brand(CardBrand.valueOf(getValue(cardData, "brand"))) + .last4(getValue(cardData, "last4")) + .fingerprint(getValue(cardData, "fingerprint")) + .funding(CardFunding.valueOf(getValue(cardData, "funding"))) + .country(getValue(cardData, "country")) + .currency(getValue(cardData, "currency")) + .id(getValue(cardData, "id")) + .build(); + } + + private static String enumName(Enum object) { + if (object != null) { + return object.name(); +// return object.name().toLowerCase(Locale.US); + } + return null; + } @NonNull public static WritableMap convertSourceToWritableMap(@Nullable Source source) { @@ -216,28 +232,31 @@ public static WritableMap convertSourceToWritableMap(@Nullable Source source) { return newSource; } + newSource.putString("sourceId", source.getId()); newSource.putInt("amount", source.getAmount().intValue()); newSource.putInt("created", source.getCreated().intValue()); - newSource.putMap("codeVerification", convertCodeVerificationToWritableMap(source.getCodeVerification())); + newSource.putMap( + "codeVerification", convertCodeVerificationToWritableMap(source.getCodeVerification())); newSource.putString("currency", source.getCurrency()); - newSource.putString("flow", source.getFlow()); + newSource.putString("flow", enumName(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", source.getStatus()); + newSource.putString("status", enumName(source.getStatus())); newSource.putString("type", source.getType()); newSource.putString("typeRaw", source.getTypeRaw()); - newSource.putString("usage", source.getUsage()); + newSource.putString("usage", enumName(source.getUsage())); return newSource; } @NonNull - public static WritableMap convertPaymentIntentResultToWritableMap(@Nullable PaymentIntentResult paymentIntentResult) { + public static WritableMap convertPaymentIntentResultToWritableMap( + @Nullable PaymentIntentResult paymentIntentResult) { WritableMap wm = Arguments.createMap(); if (paymentIntentResult == null) { @@ -249,16 +268,16 @@ public static WritableMap convertPaymentIntentResultToWritableMap(@Nullable Paym wm.putString("status", intent.getStatus().toString()); wm.putString("paymentIntentId", intent.getId()); -// String paymentMethodId = intent.getPaymentMethodId(); -// if (paymentMethodId != null) { -// wm.putString("paymentMethodId", paymentMethodId); -// } + // String paymentMethodId = intent.getPaymentMethodId(); + // if (paymentMethodId != null) { + // wm.putString("paymentMethodId", paymentMethodId); + // } return wm; } - @NonNull - public static WritableMap convertSetupIntentResultToWritableMap(@Nullable SetupIntentResult setupIntentResult) { + public static WritableMap convertSetupIntentResultToWritableMap( + @Nullable SetupIntentResult setupIntentResult) { WritableMap wm = Arguments.createMap(); if (setupIntentResult == null) { @@ -278,7 +297,8 @@ public static WritableMap convertSetupIntentResultToWritableMap(@Nullable SetupI } @NonNull - public static WritableMap convertPaymentMethodToWritableMap(@Nullable PaymentMethod paymentMethod) { + public static WritableMap convertPaymentMethodToWritableMap( + @Nullable PaymentMethod paymentMethod) { WritableMap wm = Arguments.createMap(); if (paymentMethod == null) { @@ -288,7 +308,7 @@ public static WritableMap convertPaymentMethodToWritableMap(@Nullable PaymentMet wm.putString("id", paymentMethod.id); wm.putInt("created", paymentMethod.created.intValue()); wm.putBoolean("livemode", paymentMethod.liveMode); - wm.putString("type", paymentMethod.type); + wm.putString("type", paymentMethod.type.name()); wm.putMap("billingDetails", convertBillingDetailsToWritableMap(paymentMethod.billingDetails)); wm.putMap("card", convertPaymentMethodCardToWritableMap(paymentMethod.card)); wm.putString("customerId", paymentMethod.customerId); @@ -298,7 +318,8 @@ public static WritableMap convertPaymentMethodToWritableMap(@Nullable PaymentMet } @NonNull - public static WritableMap convertPaymentMethodCardToWritableMap(@Nullable final PaymentMethod.Card card) { + public static WritableMap convertPaymentMethodCardToWritableMap( + @Nullable final PaymentMethod.Card card) { WritableMap wm = Arguments.createMap(); if (card == null) { @@ -307,7 +328,7 @@ public static WritableMap convertPaymentMethodCardToWritableMap(@Nullable final // Omitted (can be introduced later): card.checks, card.threeDSecureUsage, card.wallet - wm.putString("brand", card.brand); + wm.putMap("brand", convertCardBrandToWritableMap(card.brand)); wm.putString("country", card.country); wm.putInt("expMonth", card.expiryMonth); wm.putInt("expYear", card.expiryYear); @@ -317,7 +338,28 @@ public static WritableMap convertPaymentMethodCardToWritableMap(@Nullable final } @NonNull - public static WritableMap convertBillingDetailsToWritableMap(@Nullable final PaymentMethod.BillingDetails billingDetails) { + public static WritableMap convertCardBrandToWritableMap( + @Nullable final CardBrand brand) { + WritableMap wm = Arguments.createMap(); + + if (brand == null) { + return wm; + } + + final WritableNativeArray wna = new WritableNativeArray(); + wna.addAll(brand.getCvcLength()); + + wm.putString("code", brand.getCode()); + wm.putString("displayName", brand.getDisplayName()); + wm.putArray("cvcLength", wna); + wm.putInt("maxCvcLength", brand.getMaxCvcLength()); + return wm; + } + + + @NonNull + public static WritableMap convertBillingDetailsToWritableMap( + @Nullable final PaymentMethod.BillingDetails billingDetails) { WritableMap wm = Arguments.createMap(); if (billingDetails == null) { @@ -331,7 +373,6 @@ public static WritableMap convertBillingDetailsToWritableMap(@Nullable final Pay return wm; } - @NonNull public static WritableMap stringMapToWritableMap(@Nullable Map map) { WritableMap writableMap = Arguments.createMap(); @@ -348,7 +389,7 @@ public static WritableMap stringMapToWritableMap(@Nullable Map m } @NonNull - public static WritableMap convertOwnerToWritableMap(@Nullable final SourceOwner owner) { + public static WritableMap convertOwnerToWritableMap(@Nullable final Owner owner) { WritableMap map = Arguments.createMap(); if (owner == null) { @@ -386,7 +427,7 @@ public static WritableMap convertAddressToWritableMap(@Nullable final Address ad } @NonNull - public static WritableMap convertReceiverToWritableMap(@Nullable final SourceReceiver receiver) { + public static WritableMap convertReceiverToWritableMap(@Nullable final Receiver receiver) { WritableMap map = Arguments.createMap(); if (receiver == null) { @@ -402,7 +443,7 @@ public static WritableMap convertReceiverToWritableMap(@Nullable final SourceRec } @NonNull - public static WritableMap convertRedirectToWritableMap(@Nullable SourceRedirect redirect) { + public static WritableMap convertRedirectToWritableMap(@Nullable Redirect redirect) { WritableMap map = Arguments.createMap(); if (redirect == null) { @@ -410,14 +451,15 @@ public static WritableMap convertRedirectToWritableMap(@Nullable SourceRedirect } map.putString("returnUrl", redirect.getReturnUrl()); - map.putString("status", redirect.getStatus()); + map.putString("status", enumName(redirect.getStatus())); map.putString("url", redirect.getUrl()); return map; } @NonNull - public static WritableMap convertCodeVerificationToWritableMap(@Nullable SourceCodeVerification codeVerification) { + public static WritableMap convertCodeVerificationToWritableMap( + @Nullable CodeVerification codeVerification) { WritableMap map = Arguments.createMap(); if (codeVerification == null) { @@ -425,40 +467,41 @@ public static WritableMap convertCodeVerificationToWritableMap(@Nullable SourceC } map.putInt("attemptsRemaining", codeVerification.getAttemptsRemaining()); - map.putString("status", codeVerification.getStatus()); + map.putString("status", enumName(codeVerification.getStatus())); return map; } @NonNull - public static WritableMap mapToWritableMap(@Nullable Map map){ + public static WritableMap mapToWritableMap(@Nullable Map map) { WritableMap writableMap = Arguments.createMap(); if (map == null) { return writableMap; } - for (String key: map.keySet()) { + 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) { + 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()); + 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()); + 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); + map.putMap(key, (WritableNativeMap) object); } else if (argumentClass == WritableNativeArray.class) { map.putArray(key, (WritableNativeArray) object); } else { @@ -466,10 +509,12 @@ 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; + if (address == null) { + return result; + } putIfNotEmpty(result, "address1", address.getAddress1()); putIfNotEmpty(result, "address2", address.getAddress2()); @@ -488,22 +533,32 @@ public static WritableMap convertAddressToWritableMap(final UserAddress address) return result; } - public static BankAccount createBankAccount(ReadableMap accountData) { - BankAccount account = new BankAccount( - // required fields only - accountData.getString("accountNumber"), - getValue(accountData, "accountHolderName"), - getValue(accountData, "accountHolderType"), - null, - accountData.getString("countryCode"), - accountData.getString("currency"), - null, - null, - getValue(accountData, "routingNumber", "") - ); - - return account; - } + public static BankAccountTokenParams createBankAccountTokenParams(ReadableMap accountData) { + return new BankAccountTokenParams( + accountData.getString("countryCode"), + accountData.getString("currency"), + accountData.getString("accountNumber"), + Type.values()[accountData.getInt( "accountHolderType")], + getValue(accountData, "accountHolderName"), + getValue(accountData, "routingNumber", "")); + } + + // public static BankAccount createBankAccount(ReadableMap accountData) { + // BankAccount account = new BankAccount( + // // required fields only + // accountData.getString("accountNumber"), + // getValue(accountData, "accountHolderName"), + // getValue(accountData, "accountHolderType"), + // null, + // accountData.getString("countryCode"), + // accountData.getString("currency"), + // null, + // null, + // getValue(accountData, "routingNumber", "") + // ); + // + // return account; + // } public static String getStringOrNull(@NonNull ReadableMap map, @NonNull String key) { return map.hasKey(key) ? map.getString(key) : null; @@ -513,7 +568,8 @@ public static ReadableMap getMapOrNull(@NonNull ReadableMap map, @NonNull String return map.hasKey(key) ? map.getMap(key) : null; } - public static boolean getBooleanOrNull(@NonNull ReadableMap map, @NonNull String key, boolean defaultVal) { + public static boolean getBooleanOrNull( + @NonNull ReadableMap map, @NonNull String key, boolean defaultVal) { return map.hasKey(key) ? map.getBoolean(key) : defaultVal; } @@ -530,5 +586,4 @@ public static UserAddress getBillingAddress(PaymentData paymentData) { return null; } - } diff --git a/android/src/main/java/com/gettipsi/stripe/util/InitializationOptions.java b/android/src/main/java/com/gettipsi/stripe/util/InitializationOptions.java index 26e2fa66..7ad59bda 100644 --- a/android/src/main/java/com/gettipsi/stripe/util/InitializationOptions.java +++ b/android/src/main/java/com/gettipsi/stripe/util/InitializationOptions.java @@ -1,14 +1,10 @@ package com.gettipsi.stripe.util; -/** - * Created by ngoriachev on 15/03/2018. - */ - +/** Created by ngoriachev on 15/03/2018. */ public abstract class InitializationOptions { public static final String PUBLISHABLE_KEY = "publishableKey"; 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"; - } diff --git a/android/src/main/java/com/gettipsi/stripe/util/PayParams.java b/android/src/main/java/com/gettipsi/stripe/util/PayParams.java index fd3c6219..c97d768d 100644 --- a/android/src/main/java/com/gettipsi/stripe/util/PayParams.java +++ b/android/src/main/java/com/gettipsi/stripe/util/PayParams.java @@ -1,9 +1,6 @@ package com.gettipsi.stripe.util; -/** - * Created by ngoriachev on 13/03/2018. - */ - +/** Created by ngoriachev on 13/03/2018. */ public abstract class PayParams { public static final String CURRENCY_CODE = "currency_code"; @@ -16,5 +13,4 @@ public abstract class PayParams { public static final String LINE_ITEMS = "line_items"; public static final String QUANTITY = "quantity"; public static final String DESCRIPTION = "description"; - } diff --git a/android/src/main/java/com/gettipsi/stripe/util/Utils.java b/android/src/main/java/com/gettipsi/stripe/util/Utils.java index a3793e87..41860eee 100644 --- a/android/src/main/java/com/gettipsi/stripe/util/Utils.java +++ b/android/src/main/java/com/gettipsi/stripe/util/Utils.java @@ -2,21 +2,17 @@ import com.stripe.android.model.Card; -/** - * Created by dmitriy on 11/25/16 - */ - +/** Created by dmitriy on 11/25/16 */ public class Utils { - public static String validateCard(final Card card) { - if (!card.validateNumber()) { - return "The card number that you entered is invalid"; - } else if (!card.validateExpiryDate()) { - return "The expiration date that you entered is invalid"; - } else if (!card.validateCVC()) { - return "The CVC code that you entered is invalid"; - } - return null; + public static String validateCard(final Card card) { + if (!card.validateNumber()) { + return "The card number that you entered is invalid"; + } else if (!card.validateExpiryDate()) { + return "The expiration date that you entered is invalid"; + } else if (!card.validateCVC()) { + return "The CVC code that you entered is invalid"; } - + return null; + } } diff --git a/android/src/main/kotlin/de/jonasbark/stripe_payment/MethodCallHandlerImpl.kt b/android/src/main/kotlin/de/jonasbark/stripe_payment/MethodCallHandlerImpl.kt new file mode 100644 index 00000000..20b26653 --- /dev/null +++ b/android/src/main/kotlin/de/jonasbark/stripe_payment/MethodCallHandlerImpl.kt @@ -0,0 +1,81 @@ +package de.jonasbark.stripe_payment + +import android.app.Activity +import android.content.Context +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReadableMap +import com.gettipsi.stripe.StripeModule +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel + +class MethodCallHandlerImpl( + applicationContext: Context, + activity: Activity?, + activityRegistry: ActivityRegistry +) : MethodChannel.MethodCallHandler { + private val stripeModule = StripeModule(applicationContext, activity, activityRegistry) + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "setOptions" -> { + stripeModule.init( + ReadableMap(call.argument("options")), + ReadableMap(call.argument("errorCodes")) + ) + result.success(null) + } + "setStripeAccount" -> { + stripeModule.setStripeAccount(call.argument("stripeAccount")) + result.success(null) + } + "deviceSupportsAndroidPay" -> stripeModule.deviceSupportsAndroidPay(Promise(result)) + "canMakeAndroidPayPayments" -> stripeModule.canMakeAndroidPayPayments(Promise(result)) + "canMakeApplePayPayments" -> result.success(false) + "potentiallyAvailableNativePayNetworks" -> stripeModule.potentiallyAvailableNativePayNetworks(Promise(result)) + "paymentMethodFromAndroidPay" -> stripeModule.paymentMethodFromAndroidPay( + ReadableMap(call.arguments()), + Promise(result) + ) + "paymentRequestWithAndroidPay" -> stripeModule.paymentRequestWithAndroidPay( + ReadableMap(call.arguments()), + Promise(result) + ) + "paymentRequestWithCardForm" -> stripeModule.paymentRequestWithCardForm( + ReadableMap(call.arguments()), + Promise(result) + ) + "createTokenWithCard" -> stripeModule.createTokenWithCard( + ReadableMap(call.arguments()), + Promise(result) + ) + "createTokenWithBankAccount" -> stripeModule.createTokenWithBankAccount( + ReadableMap(call.arguments()), + Promise(result) + ) + "createSourceWithParams" -> stripeModule.createSourceWithParams( + ReadableMap(call.arguments()), + Promise(result) + ) + "createPaymentMethod" -> stripeModule.createPaymentMethod( + ReadableMap(call.arguments()), + Promise(result) + ) + "authenticatePaymentIntent" -> stripeModule.authenticatePaymentIntent( + ReadableMap(call.arguments()), + Promise(result) + ) + "confirmPaymentIntent" -> stripeModule.confirmPaymentIntent( + ReadableMap(call.arguments()), + Promise(result) + ) + "authenticateSetupIntent" -> stripeModule.authenticateSetupIntent( + ReadableMap(call.arguments()), + Promise(result) + ) + "confirmSetupIntent" -> stripeModule.confirmSetupIntent( + ReadableMap(call.arguments()), + Promise(result) + ) + } + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/de/jonasbark/stripepayment/PaymentDataRequest.kt b/android/src/main/kotlin/de/jonasbark/stripe_payment/PaymentDataRequest.kt similarity index 98% rename from android/src/main/kotlin/de/jonasbark/stripepayment/PaymentDataRequest.kt rename to android/src/main/kotlin/de/jonasbark/stripe_payment/PaymentDataRequest.kt index 0ab7b33e..6e82d6e2 100644 --- a/android/src/main/kotlin/de/jonasbark/stripepayment/PaymentDataRequest.kt +++ b/android/src/main/kotlin/de/jonasbark/stripe_payment/PaymentDataRequest.kt @@ -1,4 +1,4 @@ -package de.jonasbark.stripepayment +package de.jonasbark.stripe_payment import com.google.android.gms.wallet.PaymentDataRequest import com.stripe.android.GooglePayConfig diff --git a/android/src/main/kotlin/de/jonasbark/stripepayment/StripeDialog.kt b/android/src/main/kotlin/de/jonasbark/stripe_payment/StripeDialog.kt similarity index 94% rename from android/src/main/kotlin/de/jonasbark/stripepayment/StripeDialog.kt rename to android/src/main/kotlin/de/jonasbark/stripe_payment/StripeDialog.kt index 322f6fb5..5b9280f2 100644 --- a/android/src/main/kotlin/de/jonasbark/stripepayment/StripeDialog.kt +++ b/android/src/main/kotlin/de/jonasbark/stripe_payment/StripeDialog.kt @@ -1,4 +1,4 @@ -package de.jonasbark.stripepayment +package de.jonasbark.stripe_payment import android.app.DialogFragment import android.content.DialogInterface @@ -82,6 +82,8 @@ class StripeDialog : DialogFragment() { stripeInstance.createPaymentMethod( paymentMethodCreateParams, + null, + null, object : ApiResultCallback { override fun onSuccess(result: PaymentMethod) { view?.findViewById(R.id.progress)?.visibility = View.GONE @@ -91,11 +93,11 @@ class StripeDialog : DialogFragment() { dismiss() } - override fun onError(error: Exception) { + override fun onError(e: Exception) { view?.findViewById(R.id.progress)?.visibility = View.GONE view?.findViewById(R.id.buttonSave)?.visibility = View.VISIBLE view?.let { - Snackbar.make(it, error.localizedMessage, Snackbar.LENGTH_LONG) + Snackbar.make(it, e.localizedMessage, Snackbar.LENGTH_LONG) .show() } } diff --git a/android/src/main/kotlin/de/jonasbark/stripe_payment/StripePaymentPlugin.kt b/android/src/main/kotlin/de/jonasbark/stripe_payment/StripePaymentPlugin.kt new file mode 100644 index 00000000..4fd07681 --- /dev/null +++ b/android/src/main/kotlin/de/jonasbark/stripe_payment/StripePaymentPlugin.kt @@ -0,0 +1,117 @@ +package de.jonasbark.stripe_payment + +import android.app.Activity +import android.content.Context +import android.os.Handler +import androidx.annotation.CheckResult +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.PluginRegistry.ActivityResultListener +import io.flutter.plugin.common.PluginRegistry.Registrar + +interface ActivityRegistry { + fun addListener(handler: ActivityResultListener): Boolean + + @CheckResult + fun removeListener(handler: ActivityResultListener): Boolean +} + +class StripePaymentPlugin : FlutterPlugin, ActivityAware { + private var flutterPluginBinding: FlutterPluginBinding? = null + + private var methodChannel: MethodChannel? = null + + + private val mainHandler = Handler() + + @Suppress("unused") + companion object { + /** Plugin registration. */ + @JvmStatic + fun registerWith(registrar: Registrar) { + val instance = StripePaymentPlugin() + instance.startListening( + registrar.context(), + registrar.activity(), + registrar.messenger(), + object : ActivityRegistry { + override fun addListener(handler: ActivityResultListener): Boolean { + registrar.addActivityResultListener(handler) + return true + } + + override fun removeListener(handler: ActivityResultListener): Boolean { + // Not supported in V1 embedding. + return false + } + } + ) + } + } + + override fun onAttachedToEngine(binding: FlutterPluginBinding) { + this.flutterPluginBinding = binding + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) { + this.flutterPluginBinding = null + } + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + if (flutterPluginBinding == null) { + return + } + + startListening( + flutterPluginBinding!!.applicationContext, + binding.activity, + flutterPluginBinding!!.binaryMessenger, + object : ActivityRegistry { + override fun addListener(handler: ActivityResultListener): Boolean { + mainHandler.post { + binding.addActivityResultListener(handler) + } + return true + } + + + override fun removeListener(handler: ActivityResultListener): Boolean { + mainHandler.post { + binding.removeActivityResultListener(handler) + } + return true + } + } + ) + } + + override fun onDetachedFromActivityForConfigChanges() { + onDetachedFromActivity() + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + onAttachedToActivity(binding) + } + + override fun onDetachedFromActivity() { + stopListening() + } + + private fun startListening(applicationContext: Context, activity: Activity?, messenger: BinaryMessenger?, activityRegistry: ActivityRegistry) { + methodChannel = MethodChannel(messenger, "stripe_payment") + methodChannel?.setMethodCallHandler(MethodCallHandlerImpl( + applicationContext, + activity, + activityRegistry + )) + } + + private fun stopListening() { + methodChannel?.setMethodCallHandler(null) + methodChannel = null + } +} diff --git a/android/src/main/kotlin/de/jonasbark/stripepayment/StripePaymentPlugin.kt b/android/src/main/kotlin/de/jonasbark/stripepayment/StripePaymentPlugin.kt deleted file mode 100644 index f30a5475..00000000 --- a/android/src/main/kotlin/de/jonasbark/stripepayment/StripePaymentPlugin.kt +++ /dev/null @@ -1,78 +0,0 @@ -package de.jonasbark.stripepayment - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReadableMap -import com.gettipsi.stripe.StripeModule -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry.Registrar - -class StripePaymentPlugin(private val stripeModule: StripeModule) : MethodCallHandler { - - override fun onMethodCall(call: MethodCall, result: Result) { - when (call.method) { - "setOptions" -> stripeModule.init( - ReadableMap(call.argument("options")), - ReadableMap(call.argument("errorCodes")) - ) - "setStripeAccount" -> stripeModule.setStripeAccount( - call.argument("stripeAccount") - ) - "deviceSupportsAndroidPay" -> stripeModule.deviceSupportsAndroidPay(Promise(result)); - "canMakeAndroidPayPayments" -> stripeModule.canMakeAndroidPayPayments(Promise(result)); - "paymentRequestWithAndroidPay" -> stripeModule.paymentRequestWithAndroidPay( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "paymentRequestWithCardForm" -> stripeModule.paymentRequestWithCardForm( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "createTokenWithCard" -> stripeModule.createTokenWithCard( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "createTokenWithBankAccount" -> stripeModule.createTokenWithBankAccount( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "createSourceWithParams" -> stripeModule.createSourceWithParams( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "createPaymentMethod" -> stripeModule.createPaymentMethod( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "authenticatePaymentIntent" -> stripeModule.authenticatePaymentIntent( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "confirmPaymentIntent" -> stripeModule.confirmPaymentIntent( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "authenticateSetupIntent" -> stripeModule.authenticateSetupIntent( - ReadableMap(call.arguments as Map), - Promise(result) - ) - "confirmSetupIntent" -> stripeModule.confirmSetupIntent( - ReadableMap(call.arguments as Map), - Promise(result) - ) - } - } - - companion object { - - @JvmStatic - fun registerWith(registrar: Registrar) { - val channel = MethodChannel(registrar.messenger(), "stripe_payment") - val stripeModule = StripeModule(registrar, registrar.activity()) - val plugin = StripePaymentPlugin(stripeModule) - channel.setMethodCallHandler(plugin) - } - } -} diff --git a/build.yaml b/build.yaml new file mode 100644 index 00000000..7f7bca4e --- /dev/null +++ b/build.yaml @@ -0,0 +1,8 @@ +targets: + $default: + builders: + json_serializable: + options: + any_map: true + explicit_to_json: true + include_if_null: false \ No newline at end of file diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 2650a51b..9ffda211 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -16,7 +16,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 29 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -29,7 +29,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "de.jonasbark.stripepaymentexample" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 29 versionCode 1 multiDexEnabled true @@ -44,6 +44,9 @@ android { signingConfig signingConfigs.debug } debug { + ndk { + abiFilters 'arm64-v8a' + } } profile { @@ -62,8 +65,11 @@ flutter { } dependencies { - api 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.70' + api 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20' implementation 'androidx.annotation:annotation:1.1.0' def multidex_version = "2.0.1" implementation "androidx.multidex:multidex:$multidex_version" + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/example/android/app/src/androidTest/java/de/jonasbark/stripe_payment/EmbeddingV1ActivityTest.java b/example/android/app/src/androidTest/java/de/jonasbark/stripe_payment/EmbeddingV1ActivityTest.java new file mode 100644 index 00000000..4691770a --- /dev/null +++ b/example/android/app/src/androidTest/java/de/jonasbark/stripe_payment/EmbeddingV1ActivityTest.java @@ -0,0 +1,15 @@ +package de.jonasbark.stripe_payment; + +import androidx.test.rule.ActivityTestRule; +import de.jonasbark.stripe_payment_example.EmbeddingV1Activity; +import dev.flutter.plugins.e2e.FlutterTestRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterTestRunner.class) +public class EmbeddingV1ActivityTest { + + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(EmbeddingV1Activity.class); +} diff --git a/example/android/app/src/androidTest/java/de/jonasbark/stripe_payment/MainActivityTest.java b/example/android/app/src/androidTest/java/de/jonasbark/stripe_payment/MainActivityTest.java new file mode 100644 index 00000000..b2ca48ad --- /dev/null +++ b/example/android/app/src/androidTest/java/de/jonasbark/stripe_payment/MainActivityTest.java @@ -0,0 +1,14 @@ +package de.jonasbark.stripe_payment; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterTestRunner.class) +public class MainActivityTest { + + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index b0b4292f..eda7234d 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="de.jonasbark.stripe_payment_example"> diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index df520cc4..a9477e12 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="de.jonasbark.stripe_payment_example"> - + + + + diff --git a/example/android/app/src/main/java/de/jonasbark/stripe_payment_example/EmbeddingV1Activity.java b/example/android/app/src/main/java/de/jonasbark/stripe_payment_example/EmbeddingV1Activity.java new file mode 100644 index 00000000..545a49a1 --- /dev/null +++ b/example/android/app/src/main/java/de/jonasbark/stripe_payment_example/EmbeddingV1Activity.java @@ -0,0 +1,15 @@ +package de.jonasbark.stripe_payment_example; + +import android.os.Bundle; +import de.jonasbark.stripe_payment.StripePaymentPlugin; +import dev.flutter.plugins.integration_test.IntegrationTestPlugin; +import io.flutter.app.FlutterActivity; + +public class EmbeddingV1Activity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + StripePaymentPlugin.registerWith(registrarFor("de.jonasbark.stripe_payment.StripePaymentPlugin")); + IntegrationTestPlugin.registerWith(registrarFor("dev.flutter.plugins.e2e.E2EPlugin")); + } +} \ No newline at end of file diff --git a/example/android/app/src/main/kotlin/jonasbark/de/stripepaymentexample/MainActivity.kt b/example/android/app/src/main/kotlin/jonasbark/de/stripepaymentexample/MainActivity.kt deleted file mode 100644 index 2899e47a..00000000 --- a/example/android/app/src/main/kotlin/jonasbark/de/stripepaymentexample/MainActivity.kt +++ /dev/null @@ -1,16 +0,0 @@ -package jonasbark.de.stripepaymentexample - -import android.os.Bundle -import io.flutter.app.FlutterActivity -import io.flutter.plugins.GeneratedPluginRegistrant - -class MainActivity : FlutterActivity() { - - - @Override - protected override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - GeneratedPluginRegistrant.registerWith(this) - } -} - diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index b0b4292f..eda7234d 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="de.jonasbark.stripe_payment_example"> diff --git a/example/android/build.gradle b/example/android/build.gradle index 09661dbb..45d37ba1 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.70' + ext.kotlin_version = '1.3.72' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' + classpath 'com.android.tools.build:gradle:3.6.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/ios/AppFrameworkInfo.plist b/example/ios/AppFrameworkInfo.plist new file mode 100644 index 00000000..9367d483 --- /dev/null +++ b/example/ios/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/example/ios/Podfile b/example/ios/Podfile index 9411102b..313ea4a1 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '10.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 6ccf640c..574d9aba 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -141,12 +141,10 @@ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( 758DE840BDB7F87DC11884BA /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 7EB6A04D984C431F7857F3D5 /* [CP] Embed Pods Frameworks */, ); buildRules = ( @@ -164,7 +162,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1230; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -206,20 +204,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; 758DE840BDB7F87DC11884BA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -247,36 +231,16 @@ buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", - "${BUILT_PRODUCTS_DIR}/Stripe/Stripe.framework", - "${BUILT_PRODUCTS_DIR}/stripe_payment/stripe_payment.framework", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Stripe.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/stripe_payment.framework", + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; /* End PBXShellScriptBuildPhase section */ @@ -335,6 +299,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -353,7 +318,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -368,18 +333,23 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GPGRWN6G4J; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = de.jonasbark.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -392,6 +362,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -411,6 +382,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -435,7 +407,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -447,6 +419,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -466,6 +439,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -484,11 +458,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -496,7 +471,7 @@ }; 97C147061CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + baseConfigurationReference = 08AEEC3FCD4A8F04F4DE064C /* Pods-Runner.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -507,11 +482,15 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = de.jonasbark.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -523,22 +502,27 @@ }; 97C147071CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = 9ADCBB605DAE98D853C28070 /* Pods-Runner.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GPGRWN6G4J; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = de.jonasbark.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cf..8b9c2978 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - + BuildSystemType + Latest PreviewsEnabled diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index a060db61..e45da6a5 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleVersion - $(FLUTTER_BUILD_NUMBER) + $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/example/lib/main.dart b/example/lib/main.dart index 3a87ba0d..6e1bbb40 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:stripe_payment/stripe_payment.dart'; import 'dart:io'; +import 'package:http/http.dart' as http; void main() { runApp(new MyApp()); @@ -17,7 +18,7 @@ class _MyAppState extends State { Token _paymentToken; PaymentMethod _paymentMethod; String _error; - final String _currentSecret = null; //set this yourself, e.g using curl + String _currentSecret; //set this yourself, e.g using curl PaymentIntentResult _paymentIntent; Source _source; @@ -35,12 +36,16 @@ class _MyAppState extends State { initState() { super.initState(); - StripePayment.setOptions( - StripeOptions(publishableKey: "pk_test_aSaULNS8cJU6Tvo20VAXy6rp", merchantId: "Test", androidPayMode: 'test')); + StripePayment.setOptions(StripeOptions( + publishableKey: "pk_test_Td50zsO17OMeDeHFw7VWoUZO00Ky35bmfY", + merchantId: "Test", + androidPayMode: 'test', + )); } void setError(dynamic error) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(error.toString()))); + _scaffoldKey.currentState + .showSnackBar(SnackBar(content: Text(error.toString()))); setState(() { _error = error.toString(); }); @@ -80,7 +85,8 @@ class _MyAppState extends State { currency: 'eur', returnURL: 'example://stripe-redirect', )).then((source) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Received ${source.sourceId}'))); + _scaffoldKey.currentState.showSnackBar( + SnackBar(content: Text('Received ${source.sourceId}'))); setState(() { _source = source; }); @@ -91,8 +97,11 @@ class _MyAppState extends State { RaisedButton( child: Text("Create Token with Card Form"), onPressed: () { - StripePayment.paymentRequestWithCardForm(CardFormPaymentRequest()).then((paymentMethod) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Received ${paymentMethod.id}'))); + StripePayment.paymentRequestWithCardForm( + CardFormPaymentRequest()) + .then((paymentMethod) { + _scaffoldKey.currentState.showSnackBar( + SnackBar(content: Text('Received ${paymentMethod.id}'))); setState(() { _paymentMethod = paymentMethod; }); @@ -105,7 +114,8 @@ class _MyAppState extends State { StripePayment.createTokenWithCard( testCard, ).then((token) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Received ${token.tokenId}'))); + _scaffoldKey.currentState.showSnackBar( + SnackBar(content: Text('Received ${token.tokenId}'))); setState(() { _paymentToken = token; }); @@ -121,10 +131,12 @@ class _MyAppState extends State { card: testCard, ), ).then((paymentMethod) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Received ${paymentMethod.id}'))); + _scaffoldKey.currentState.showSnackBar( + SnackBar(content: Text('Received ${paymentMethod.id}'))); setState(() { _paymentMethod = paymentMethod; }); + _buildSecret(_paymentMethod.id); }).catchError(setError); }, ), @@ -140,7 +152,8 @@ class _MyAppState extends State { ), ), ).then((paymentMethod) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Received ${paymentMethod.id}'))); + _scaffoldKey.currentState.showSnackBar(SnackBar( + content: Text('Received ${paymentMethod.id}'))); setState(() { _paymentMethod = paymentMethod; }); @@ -159,8 +172,9 @@ class _MyAppState extends State { paymentMethodId: _paymentMethod.id, ), ).then((paymentIntent) { - _scaffoldKey.currentState - .showSnackBar(SnackBar(content: Text('Received ${paymentIntent.paymentIntentId}'))); + _scaffoldKey.currentState.showSnackBar(SnackBar( + content: Text( + 'Received ${paymentIntent.paymentIntentId}'))); setState(() { _paymentIntent = paymentIntent; }); @@ -172,9 +186,12 @@ class _MyAppState extends State { onPressed: _currentSecret == null ? null : () { - StripePayment.authenticatePaymentIntent(clientSecret: _currentSecret).then((paymentIntent) { - _scaffoldKey.currentState - .showSnackBar(SnackBar(content: Text('Received ${paymentIntent.paymentIntentId}'))); + StripePayment.authenticatePaymentIntent( + clientSecret: _currentSecret) + .then((paymentIntent) { + _scaffoldKey.currentState.showSnackBar(SnackBar( + content: Text( + 'Received ${paymentIntent.paymentIntentId}'))); setState(() { _paymentIntent = paymentIntent; }); @@ -182,6 +199,21 @@ class _MyAppState extends State { }, ), Divider(), + RaisedButton( + child: Text("Potentially Available Networks"), + onPressed: () async { + final networks = + await StripePayment.potentiallyAvailableNativePayNetworks(); + print('networks: $networks'); + }, + ), + RaisedButton( + child: Text("Can Make Native Payment"), + onPressed: () async { + final res = await StripePayment.canMakeNativePayPayments([]); + print('canMakeNativePayPayments: $res'); + }, + ), RaisedButton( child: Text("Native payment"), onPressed: () { @@ -205,7 +237,8 @@ class _MyAppState extends State { ), ).then((token) { setState(() { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Received ${token.tokenId}'))); + _scaffoldKey.currentState.showSnackBar( + SnackBar(content: Text('Received ${token.tokenId}'))); _paymentToken = token; }); }).catchError(setError); @@ -215,7 +248,8 @@ class _MyAppState extends State { child: Text("Complete Native Payment"), onPressed: () { StripePayment.completeNativePayRequest().then((_) { - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text('Completed successfully'))); + _scaffoldKey.currentState.showSnackBar( + SnackBar(content: Text('Completed successfully'))); }).catchError(setError); }, ), @@ -228,19 +262,22 @@ class _MyAppState extends State { Divider(), Text('Current token:'), Text( - JsonEncoder.withIndent(' ').convert(_paymentToken?.toJson() ?? {}), + JsonEncoder.withIndent(' ') + .convert(_paymentToken?.toJson() ?? {}), style: TextStyle(fontFamily: "Monospace"), ), Divider(), Text('Current payment method:'), Text( - JsonEncoder.withIndent(' ').convert(_paymentMethod?.toJson() ?? {}), + JsonEncoder.withIndent(' ') + .convert(_paymentMethod?.toJson() ?? {}), style: TextStyle(fontFamily: "Monospace"), ), Divider(), Text('Current payment intent:'), Text( - JsonEncoder.withIndent(' ').convert(_paymentIntent?.toJson() ?? {}), + JsonEncoder.withIndent(' ') + .convert(_paymentIntent?.toJson() ?? {}), style: TextStyle(fontFamily: "Monospace"), ), Divider(), @@ -250,4 +287,34 @@ class _MyAppState extends State { ), ); } + + _buildSecret(String paymentMethodId) async { + String json = ''' + { + "useStripeSdk":true, + "paymentMethodId":"$paymentMethodId", + "currency":"usd", + "items": [ + {"id":"photo_subscription"} + ] + } + '''; + + final url = Uri.http('192.168.1.189:4242', '/create-payment-intent'); + + final response = await http.post( + url, + headers: {'Content-Type': "application/json; charset=utf-8"}, + body: json, + ); + print(response.body); + final jsonResponse = jsonDecode(response.body); + final String payError = jsonResponse['error']; + final String clientSecret = jsonResponse['clientSecret']; + final String requiresAction = jsonResponse['requiresAction']; + + setState(() { + _currentSecret = clientSecret; + }); + } } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9aabf19e..2c8320a7 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -2,57 +2,19 @@ name: stripe_payment_example description: Demonstrates how to use the stripe_payment plugin. dependencies: + http: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - dev_dependencies: flutter_test: sdk: flutter stripe_payment: path: ../ - -# For information on the generic Dart part of this file, see the -# following page: https://www.dartlang.org/tools/pub/pubspec - -# The following section is specific to Flutter. + integration_test: ^1.0.1 + flutter_driver: + sdk: flutter + flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.io/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.io/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages diff --git a/example/test_driver/stripe_payment_e2e.dart b/example/test_driver/stripe_payment_e2e.dart new file mode 100644 index 00000000..1a9fb779 --- /dev/null +++ b/example/test_driver/stripe_payment_e2e.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:stripe_payment/stripe_payment.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Creates a test token', (WidgetTester tester) async { + await StripePayment.setOptions(StripeOptions(publishableKey: 'sk_test')); + }); +} diff --git a/example/test_driver/stripe_payment_e2e_test.dart b/example/test_driver/stripe_payment_e2e_test.dart new file mode 100644 index 00000000..10acaa69 --- /dev/null +++ b/example/test_driver/stripe_payment_e2e_test.dart @@ -0,0 +1,5 @@ +import 'dart:async'; + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() async => integrationDriver(); diff --git a/ios/Classes/StripePaymentPlugin.m b/ios/Classes/StripePaymentPlugin.m index de8e879f..5c12bf9a 100644 --- a/ios/Classes/StripePaymentPlugin.m +++ b/ios/Classes/StripePaymentPlugin.m @@ -1,5 +1,5 @@ #import "StripePaymentPlugin.h" -#import +#import #import "TPSStripeManager.h" @implementation StripePaymentPlugin { @@ -24,14 +24,16 @@ - (instancetype)init } - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - + id rejecter = ^(NSString *code, NSString *message, NSError *error) { result([FlutterError errorWithCode:code ?: @"-" message:message details:error.localizedDescription]); }; if ([@"setOptions" isEqualToString: call.method]) { [stripeModule init:call.arguments[@"options"] errorCodes:call.arguments[@"errorCodes"]]; + result(nil); } else if ([@"setStripeAccount" isEqualToString:call.method]) { [stripeModule setStripeAccount:call.arguments[@"stripeAccount"]]; + result(nil); } else if ([@"deviceSupportsApplePay" isEqualToString:call.method]) { [stripeModule deviceSupportsApplePay:result rejecter:rejecter]; } else if ([@"canMakeApplePayPayments" isEqualToString:call.method]) { @@ -62,6 +64,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [stripeModule paymentRequestWithCardForm:call.arguments resolver:result rejecter:rejecter]; } else if ([@"paymentRequestWithApplePay" isEqualToString:call.method]) { [stripeModule paymentRequestWithApplePay:call.arguments[@"items"] withOptions:call.arguments[@"options"] resolver:result rejecter:rejecter]; + } else if ([@"paymentMethodFromApplePay" isEqualToString:call.method]) { + [stripeModule paymentMethodFromApplePay:call.arguments[@"items"] withOptions:call.arguments[@"options"] resolver:result rejecter:rejecter]; } else if ([@"openApplePaySetup" isEqualToString:call.method]) { [stripeModule openApplePaySetup]; } diff --git a/ios/Classes/TPSStripeManager.h b/ios/Classes/TPSStripeManager.h index 4c32cb6d..e6c6e5ec 100644 --- a/ios/Classes/TPSStripeManager.h +++ b/ios/Classes/TPSStripeManager.h @@ -8,7 +8,7 @@ #import #import -#import +#import #import "RCTConvert.h" /** @@ -91,6 +91,11 @@ typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject; +-(void)paymentMethodFromApplePay:(NSArray *)items + withOptions:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject; + -(void)openApplePaySetup; @end diff --git a/ios/Classes/TPSStripeManager.m b/ios/Classes/TPSStripeManager.m index 78143ced..8c1bc854 100644 --- a/ios/Classes/TPSStripeManager.m +++ b/ios/Classes/TPSStripeManager.m @@ -7,7 +7,9 @@ // #import "TPSStripeManager.h" -#import +#import + +#import #import "TPSError.h" #import "TPSStripeManager+Constants.h" @@ -263,6 +265,7 @@ @interface StripeModule () RCTPromiseRejectBlock promiseRejector; BOOL requestIsCompleted; + BOOL applePayShouldCreatePaymentMethod; void (^applePayCompletion)(PKPaymentAuthorizationStatus); NSError *applePayStripeError; @@ -301,12 +304,12 @@ -(void)init:(NSDictionary *)options errorCodes:(NSDictionary *)errors { publishableKey = options[@"publishableKey"]; merchantId = options[@"merchantId"]; errorCodes = errors; - [Stripe setDefaultPublishableKey:publishableKey]; + [StripeAPI setDefaultPublishableKey:publishableKey]; } -(void)setStripeAccount:(NSString *)_stripeAccount { NSString *_account; - if (_stripeAccount && _stripeAccount != [NSNull null]) { + if (_stripeAccount) { _account = _stripeAccount; } stripeAccount = _account; @@ -332,7 +335,7 @@ -(void)potentiallyAvailableNativePayNetworks:(RCTPromiseResolveBlock)resolve NSArray *paymentNetworksStrings = [StripeModule applePaySupportedPaymentNetworksStrings]; NSArray *networks = [self paymentNetworks:paymentNetworksStrings]; - resolve([PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:networks] ? paymentNetworksStrings : nil); + resolve([PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:networks] ? paymentNetworksStrings : @[]); } @@ -813,9 +816,28 @@ -(void)paymentRequestWithCardForm:(NSDictionary *)options } -(void)paymentRequestWithApplePay:(NSArray *)items - withOptions:(NSDictionary *)options - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject { + withOptions:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject { + applePayShouldCreatePaymentMethod = NO; + + [self startApplePay:items withOptions:options resolver:resolve rejecter:reject]; +} + +-(void)paymentMethodFromApplePay:(NSArray *)items + withOptions:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject { + applePayShouldCreatePaymentMethod = YES; + + [self startApplePay:items withOptions:options resolver:resolve rejecter:reject]; +} + +-(void)startApplePay:(NSArray *)items + withOptions:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject { + if(!requestIsCompleted) { NSDictionary *error = [errorCodes valueForKey:kErrorKeyBusy]; reject(error[kErrorKeyCode], error[kErrorKeyDescription], nil); @@ -855,7 +877,7 @@ -(void)paymentRequestWithApplePay:(NSArray *)items [summaryItems addObject:summaryItem]; } - PKPaymentRequest *paymentRequest = [Stripe paymentRequestWithMerchantIdentifier:merchantId country:countryCode currency:currencyCode]; + PKPaymentRequest *paymentRequest = [StripeAPI paymentRequestWithMerchantIdentifier:merchantId country:countryCode currency:currencyCode]; [paymentRequest setRequiredShippingAddressFields:requiredShippingAddressFields]; [paymentRequest setRequiredBillingAddressFields:requiredBillingAddressFields]; @@ -1101,7 +1123,6 @@ - (NSDictionary*)convertPaymentMethod:(STPPaymentMethod*)method { TPSStripeParam(PaymentMethod, billingDetails): [self convertPaymentMethodBillingDetails: method.billingDetails] ?: NSNull.null, TPSStripeParam(PaymentMethod, card): [self convertPaymentMethodCard: method.card] ?: NSNull.null, TPSEntry(customerId), - TPSEntry(metadata), }; #undef TPSEntry } @@ -1182,7 +1203,7 @@ - (void)resetApplePayCallback { } - (BOOL)canSubmitPaymentRequest:(PKPaymentRequest *)paymentRequest rejecter:(RCTPromiseRejectBlock)reject { - if (![Stripe deviceSupportsApplePay]) { + if (![StripeAPI deviceSupportsApplePay]) { NSDictionary *error = [errorCodes valueForKey:kErrorKeyDeviceNotSupportsNativePay]; reject(error[kErrorKeyCode], error[kErrorKeyDescription], nil); return NO; @@ -1210,7 +1231,7 @@ - (BOOL)canSubmitPaymentRequest:(PKPaymentRequest *)paymentRequest rejecter:(RCT - (void)addCardViewController:(STPAddCardViewController *)addCardViewController didCreatePaymentMethod:(STPPaymentMethod *)paymentMethod - completion:(STPErrorBlock)completion { + completion:(void (^)(NSError * _Nullable))completion { [RCTPresentedViewController() dismissViewControllerAnimated:YES completion:nil]; requestIsCompleted = YES; @@ -1237,27 +1258,52 @@ - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController applePayCompletion = completion; STPAPIClient *stripeAPIClient = [self newAPIClient]; + + if (applePayShouldCreatePaymentMethod) { + [stripeAPIClient createPaymentMethodWithPayment:payment completion:^(STPPaymentMethod * _Nullable paymentMethod, NSError * _Nullable error) { + self->requestIsCompleted = YES; - [stripeAPIClient createTokenWithPayment:payment completion:^(STPToken * _Nullable token, NSError * _Nullable error) { - self->requestIsCompleted = YES; + if (error) { + // Save for deffered use + self->applePayStripeError = error; + [self resolveApplePayCompletion:PKPaymentAuthorizationStatusFailure]; + } else { + NSDictionary *result = [self convertPaymentMethod:paymentMethod]; +// NSDictionary *extra = @{ +// @"billingContact": [self contactDetails:payment.billingContact] ?: [NSNull null], +// @"shippingContact": [self contactDetails:payment.shippingContact] ?: [NSNull null], +// @"shippingMethod": [self shippingDetails:payment.shippingMethod] ?: [NSNull null] +// }; +// +// [result setValue:extra forKey:@"extra"]; - if (error) { - // Save for deffered use - self->applePayStripeError = error; - [self resolveApplePayCompletion:PKPaymentAuthorizationStatusFailure]; - } else { - NSDictionary *result = [self convertTokenObject:token]; - NSDictionary *extra = @{ - @"billingContact": [self contactDetails:payment.billingContact] ?: [NSNull null], - @"shippingContact": [self contactDetails:payment.shippingContact] ?: [NSNull null], - @"shippingMethod": [self shippingDetails:payment.shippingMethod] ?: [NSNull null] - }; + [self resolvePromise:result]; + } - [result setValue:extra forKey:@"extra"]; + }]; - [self resolvePromise:result]; - } - }]; + } else { + [stripeAPIClient createTokenWithPayment:payment completion:^(STPToken * _Nullable token, NSError * _Nullable error) { + self->requestIsCompleted = YES; + + if (error) { + // Save for deffered use + self->applePayStripeError = error; + [self resolveApplePayCompletion:PKPaymentAuthorizationStatusFailure]; + } else { + NSDictionary *result = [self convertTokenObject:token]; + NSDictionary *extra = @{ + @"billingContact": [self contactDetails:payment.billingContact] ?: [NSNull null], + @"shippingContact": [self contactDetails:payment.shippingContact] ?: [NSNull null], + @"shippingMethod": [self shippingDetails:payment.shippingMethod] ?: [NSNull null] + }; + + [result setValue:extra forKey:@"extra"]; + + [self resolvePromise:result]; + } + }]; + } } @@ -1294,7 +1340,7 @@ - (STPAPIClient *)newAPIClient { url:TPSAppInfoURL]; }); - STPAPIClient * client = [[STPAPIClient alloc] initWithPublishableKey:[Stripe defaultPublishableKey]]; + STPAPIClient * client = [[STPAPIClient alloc] initWithPublishableKey:[StripeAPI defaultPublishableKey]]; client.appInfo = info; client.stripeAccount = stripeAccount; @@ -1369,7 +1415,7 @@ - (NSDictionary *)convertSourceObject:(STPSource*)source { [result setValue:@(source.livemode) forKey:@"livemode"]; [result setValue:source.amount forKey:@"amount"]; [result setValue:source.stripeID forKey:@"sourceId"]; - + // Flow [result setValue:[self sourceFlow:source.flow] forKey:@"flow"]; @@ -1487,7 +1533,7 @@ - (NSString *)cardBrandAsBrandSlug:(STPCardBrand)inputBrand { return @"discover"; case STPCardBrandDinersClub: return @"diners"; - case STPCardBrandMasterCard: + case STPCardBrandMastercard: return @"mastercard"; case STPCardBrandUnknown: default: @@ -1497,7 +1543,7 @@ - (NSString *)cardBrandAsBrandSlug:(STPCardBrand)inputBrand { /// API: https://stripe.com/docs/api/cards/object#card_object-brand - (NSString *)cardBrandAsPresentableBrandString:(STPCardBrand)inputBrand { - return STPStringFromCardBrand(inputBrand); + return [STPCardBrandUtilities stringFromCardBrand:inputBrand]; } - (NSString *)cardFunding:(STPCardFundingType)inputFunding { @@ -1528,6 +1574,23 @@ - (NSString *)card3DSecureStatus:(STPSourceCard3DSecureStatus)inputStatus { } } +- (NSString *)sourceStatus:(STPSourceStatus)status { + switch (status) { + case STPSourceStatusPending: + return @"pending"; + case STPSourceStatusChargeable: + return @"chargeable"; + case STPSourceStatusConsumed: + return @"consumed"; + case STPSourceStatusCanceled: + return @"canceled"; + case STPSourceStatusFailed: + return @"failed"; + case STPSourceStatusUnknown: + return @""; + } +} + - (NSString *)sourceFlow:(STPSourceFlow)inputFlow { switch (inputFlow) { case STPSourceFlowNone: @@ -1572,31 +1635,13 @@ - (NSString *)sourceVerificationStatus:(STPSourceVerificationStatus)inputStatus } } -- (NSString *)sourceStatus:(STPSourceStatus)inputStatus { - switch (inputStatus) { - case STPSourceStatusPending: - return @"pending"; - case STPSourceStatusChargeable: - return @"chargable"; - case STPSourceStatusConsumed: - return @"consumed"; - case STPSourceStatusCanceled: - return @"canceled"; - case STPSourceStatusFailed: - return @"failed"; - case STPSourceStatusUnknown: - default: - return @"unknown"; - } -} - - (NSString *)sourceType:(STPSourceType)inputType { switch (inputType) { case STPSourceTypeBancontact: return @"bancontact"; case STPSourceTypeGiropay: return @"giropay"; - case STPSourceTypeIDEAL: + case STPSourceTypeiDEAL: return @"ideal"; case STPSourceTypeSEPADebit: return @"sepaDebit"; @@ -1816,14 +1861,75 @@ - (PKPaymentNetwork)paymentNetwork:(NSString *)paymentNetworkString { dispatch_once(&onceToken, ^{ NSMutableDictionary *mutableMap = [@{} mutableCopy]; + if ((&PKPaymentNetworkAmex) != NULL) { mutableMap[TPSPaymentNetworkAmex] = PKPaymentNetworkAmex; } + + if ((&PKPaymentNetworkChinaUnionPay) != NULL) { + mutableMap[TPSPaymentNetworkChinaUnionPay] = PKPaymentNetworkChinaUnionPay; + } if ((&PKPaymentNetworkDiscover) != NULL) { mutableMap[TPSPaymentNetworkDiscover] = PKPaymentNetworkDiscover; } + if (@available(iOS 12.0, *)) { + if ((&PKPaymentNetworkEftpos) != NULL) { + mutableMap[TPSPaymentNetworkEftpos] = PKPaymentNetworkEftpos; + } + + if ((&PKPaymentNetworkElectron) != NULL) { + mutableMap[TPSPaymentNetworkElectron] = PKPaymentNetworkElectron; + } + + } + + if (@available(iOS 12.1.1, *)) { + if ((&PKPaymentNetworkElo) != NULL) { + mutableMap[TPSPaymentNetworkElo] = PKPaymentNetworkElo; + } + if ((&PKPaymentNetworkMada) != NULL) { + mutableMap[TPSPaymentNetworkIDCredit] = PKPaymentNetworkMada; + } + } + + if ((&PKPaymentNetworkInterac) != NULL) { + mutableMap[TPSPaymentNetworkIDCredit] = PKPaymentNetworkInterac; + } + if (@available(iOS 10.1, *)) { + if ((&PKPaymentNetworkJCB) != NULL) { + mutableMap[TPSPaymentNetworkIDCredit] = PKPaymentNetworkJCB; + } + } else { + // Fallback on earlier versions + } + + if (@available(iOS 12.0, *)) { + if ((&PKPaymentNetworkMaestro) != NULL) { + mutableMap[TPSPaymentNetworkIDCredit] = PKPaymentNetworkMaestro; + } + } else { + // Fallback on earlier versions + } + if ((&PKPaymentNetworkPrivateLabel) != NULL) { + mutableMap[TPSPaymentNetworkPrivateLabel] = PKPaymentNetworkPrivateLabel; + } + if (@available(iOS 10.3, *)) { + if ((&PKPaymentNetworkQuicPay) != NULL) { + mutableMap[TPSPaymentNetworkQuicPay] = PKPaymentNetworkQuicPay; + } + } else { + // Fallback on earlier versions + } + if (@available(iOS 10.1, *)) { + if ((&PKPaymentNetworkSuica) != NULL) { + mutableMap[TPSPaymentNetworkQuicPay] = PKPaymentNetworkSuica; + } + } else { + // Fallback on earlier versions + } + if ((&PKPaymentNetworkMasterCard) != NULL) { mutableMap[TPSPaymentNetworkMasterCard] = PKPaymentNetworkMasterCard; } diff --git a/ios/stripe_payment.podspec b/ios/stripe_payment.podspec index 89bc9443..fdd18d79 100644 --- a/ios/stripe_payment.podspec +++ b/ios/stripe_payment.podspec @@ -15,6 +15,7 @@ A new flutter plugin project. s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'Stripe', '~> 19.4.0' - s.ios.deployment_target = '10.0' + s.dependency 'Stripe', '>= 21.1.0' + s.ios.deployment_target = '11.0' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } end diff --git a/lib/src/android/line_item.dart b/lib/src/android/line_item.dart new file mode 100644 index 00000000..a5a589de --- /dev/null +++ b/lib/src/android/line_item.dart @@ -0,0 +1,33 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'line_item.g.dart'; + +@JsonSerializable() +class LineItem { + @JsonKey(name: 'currency_code') + final String currencyCode; + + @JsonKey(name: 'description') + final String description; + + @JsonKey(name: 'quantity') + final String quantity; + + @JsonKey(name: 'total_price') + final String totalPrice; + + @JsonKey(name: 'unit_price') + final String unitPrice; + + const LineItem({ + this.currencyCode, + this.description, + this.quantity, + this.totalPrice, + this.unitPrice, + }); + + factory LineItem.fromJson(Map json) => + _$LineItemFromJson(json); + Map toJson() => _$LineItemToJson(this); +} diff --git a/lib/src/android/line_item.g.dart b/lib/src/android/line_item.g.dart new file mode 100644 index 00000000..d5644d29 --- /dev/null +++ b/lib/src/android/line_item.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'line_item.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LineItem _$LineItemFromJson(Map json) { + return LineItem( + currencyCode: json['currency_code'] as String, + description: json['description'] as String, + quantity: json['quantity'] as String, + totalPrice: json['total_price'] as String, + unitPrice: json['unit_price'] as String, + ); +} + +Map _$LineItemToJson(LineItem instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('currency_code', instance.currencyCode); + writeNotNull('description', instance.description); + writeNotNull('quantity', instance.quantity); + writeNotNull('total_price', instance.totalPrice); + writeNotNull('unit_price', instance.unitPrice); + return val; +} diff --git a/lib/src/android_pay_payment_request.dart b/lib/src/android_pay_payment_request.dart index a6dcd4b9..345d8eca 100644 --- a/lib/src/android_pay_payment_request.dart +++ b/lib/src/android_pay_payment_request.dart @@ -1,75 +1,48 @@ import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'android/line_item.dart'; + +part 'android_pay_payment_request.g.dart'; + +@JsonSerializable() class AndroidPayPaymentRequest { - bool billingAddressRequired; - String currencyCode; - List lineItems; - bool shippingAddressRequired; - List shippingCountries; - String totalPrice; + @JsonKey(name: 'billing_address_required') + final bool billingAddressRequired; - AndroidPayPaymentRequest( - {this.billingAddressRequired, - @required this.currencyCode, - this.lineItems, - this.shippingAddressRequired, - this.shippingCountries, - @required this.totalPrice}); + @JsonKey(name: 'currency_code') + final String currencyCode; - factory AndroidPayPaymentRequest.fromJson(Map json) { - return AndroidPayPaymentRequest( - billingAddressRequired: json['billing_address_required'], - currencyCode: json['currency_code'], - lineItems: - json['line_items'] != null ? (json['line_items'] as List).map((i) => LineItem.fromJson(i)).toList() : null, - shippingAddressRequired: json['shipping_address_required'], - shippingCountries: json['shipping_countries'] != null ? new List.from(json['shipping_countries']) : null, - totalPrice: json['total_price'], - ); - } + @JsonKey(name: 'line_items') + final List lineItems; - Map toJson() { - final Map data = new Map(); - if (this.billingAddressRequired != null) data['billing_address_required'] = this.billingAddressRequired; - if (this.currencyCode != null) data['currency_code'] = this.currencyCode; - if (this.shippingAddressRequired != null) data['shipping_address_required'] = this.shippingAddressRequired; - if (this.totalPrice != null) data['total_price'] = this.totalPrice; - if (this.lineItems != null) { - data['line_items'] = this.lineItems.map((v) => v.toJson()).toList(); - } - if (this.shippingCountries != null) { - if (this.shippingCountries != null) data['shipping_countries'] = this.shippingCountries; - } - return data; - } -} + @JsonKey(name: 'shipping_address_required') + final bool shippingAddressRequired; + + @JsonKey(name: 'shipping_countries') + final List shippingCountries; + + @JsonKey(name: 'phone_number_required') + final bool phoneNumberRequired; -class LineItem { - String currencyCode; - String description; - String quantity; - String totalPrice; - String unitPrice; + @JsonKey(name: 'email_required') + final bool emailRequired; - LineItem({this.currencyCode, this.description, this.quantity, this.totalPrice, this.unitPrice}); + @JsonKey(name: 'total_price') + final String totalPrice; - factory LineItem.fromJson(Map json) { - return LineItem( - currencyCode: json['currency_code'], - description: json['description'], - quantity: json['quantity'], - totalPrice: json['total_price'], - unitPrice: json['unit_price'], - ); - } + const AndroidPayPaymentRequest({ + this.billingAddressRequired, + this.currencyCode, + this.lineItems, + this.shippingAddressRequired, + this.shippingCountries, + this.phoneNumberRequired, + this.emailRequired, + this.totalPrice, + }); - Map toJson() { - final Map data = new Map(); - if (this.currencyCode != null) data['currency_code'] = this.currencyCode; - if (this.description != null) data['description'] = this.description; - if (this.quantity != null) data['quantity'] = this.quantity; - if (this.totalPrice != null) data['total_price'] = this.totalPrice; - if (this.unitPrice != null) data['unit_price'] = this.unitPrice; - return data; - } + factory AndroidPayPaymentRequest.fromJson(Map json) => + _$AndroidPayPaymentRequestFromJson(json); + Map toJson() => _$AndroidPayPaymentRequestToJson(this); } diff --git a/lib/src/android_pay_payment_request.g.dart b/lib/src/android_pay_payment_request.g.dart new file mode 100644 index 00000000..3e5d8041 --- /dev/null +++ b/lib/src/android_pay_payment_request.g.dart @@ -0,0 +1,49 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'android_pay_payment_request.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AndroidPayPaymentRequest _$AndroidPayPaymentRequestFromJson(Map json) { + return AndroidPayPaymentRequest( + billingAddressRequired: json['billing_address_required'] as bool, + currencyCode: json['currency_code'] as String, + lineItems: (json['line_items'] as List) + ?.map((e) => e == null + ? null + : LineItem.fromJson((e as Map)?.map( + (k, e) => MapEntry(k as String, e), + ))) + ?.toList(), + shippingAddressRequired: json['shipping_address_required'] as bool, + shippingCountries: + (json['shipping_countries'] as List)?.map((e) => e as String)?.toList(), + phoneNumberRequired: json['phone_number_required'] as bool, + emailRequired: json['email_required'] as bool, + totalPrice: json['total_price'] as String, + ); +} + +Map _$AndroidPayPaymentRequestToJson( + AndroidPayPaymentRequest instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('billing_address_required', instance.billingAddressRequired); + writeNotNull('currency_code', instance.currencyCode); + writeNotNull( + 'line_items', instance.lineItems?.map((e) => e?.toJson())?.toList()); + writeNotNull('shipping_address_required', instance.shippingAddressRequired); + writeNotNull('shipping_countries', instance.shippingCountries); + writeNotNull('phone_number_required', instance.phoneNumberRequired); + writeNotNull('email_required', instance.emailRequired); + writeNotNull('total_price', instance.totalPrice); + return val; +} diff --git a/lib/src/apple_pay_payment_request.dart b/lib/src/apple_pay_payment_request.dart index d41678f7..42c83cbf 100644 --- a/lib/src/apple_pay_payment_request.dart +++ b/lib/src/apple_pay_payment_request.dart @@ -1,4 +1,6 @@ -import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'apple_pay_payment_request.g.dart'; enum RequiredBillingAddressFields { all, name, email, phone, postal_address } @@ -6,82 +8,58 @@ enum RequiredShippingAddressFields { all, name, email, phone, postal_address } enum ShippingType { shipping, delivery, store_pickup, service_pickup } +@JsonSerializable(createFactory: false) class ApplePayPaymentOptions { - List requiredBillingAddressFields; - List requiredShippingAddressFields; - List shippingMethod; - String currencyCode; - String countryCode; - ShippingType shippingType; - List items; + final List requiredBillingAddressFields; + final List requiredShippingAddressFields; + final List shippingMethod; + final String currencyCode; + final String countryCode; + final ShippingType shippingType; + final List items; - ApplePayPaymentOptions( - {this.requiredBillingAddressFields, - this.requiredShippingAddressFields, - this.shippingMethod, - this.currencyCode, - this.countryCode, - this.shippingType, - this.items}); + const ApplePayPaymentOptions({ + this.requiredBillingAddressFields, + this.requiredShippingAddressFields, + this.shippingMethod, + this.currencyCode, + this.countryCode, + this.shippingType, + this.items, + }); - Map get json { - final Map data = new Map(); - if (this.requiredBillingAddressFields != null) { - data['requiredBillingAddressFields'] = this.requiredBillingAddressFields.map((b) => describeEnum(b)).toList(); - } - if (this.requiredShippingAddressFields != null) { - data['requiredShippingAddressFields'] = this.requiredShippingAddressFields.map((s) => describeEnum(s)).toList(); - } - if (this.shippingMethod != null) { - data['shippingMethod'] = this.shippingMethod.map((s) => s.toJson()).toList(); - } - data['currencyCode'] = currencyCode; - if (this.shippingType != null) { - data['shippingType'] = shippingType; - } - data['countryCode'] = countryCode; - return data; - } + Map toJson() => _$ApplePayPaymentOptionsToJson(this); } +@JsonSerializable() class ShippingMethod { - String amount; - String detail; - String id; - String label; - - ShippingMethod({this.amount, this.detail, this.id, this.label}); + final String amount; + final String detail; + final String id; + final String label; - factory ShippingMethod.fromJson(Map json) { - return ShippingMethod( - amount: json['amount'], - detail: json['detail'], - id: json['id'], - label: json['label'], - ); - } + const ShippingMethod({ + this.amount, + this.detail, + this.id, + this.label, + }); - Map toJson() { - final Map data = new Map(); - if (this.amount != null) data['amount'] = this.amount; - if (this.detail != null) data['detail'] = this.detail; - if (this.id != null) data['id'] = this.id; - if (this.label != null) data['label'] = this.label; - return data; - } + factory ShippingMethod.fromJson(Map json) => _$ShippingMethodFromJson(json); + Map toJson() => _$ShippingMethodToJson(this); } +@JsonSerializable(createFactory: false) class ApplePayItem { - String label; - String amount; - String type; - ApplePayItem({this.label, this.amount, this.type}); + final String label; + final String amount; + final String type; + + const ApplePayItem({ + this.label, + this.amount, + this.type, + }); - Map get json { - final Map data = new Map(); - if (this.label != null) data['label'] = this.label; - if (this.amount != null) data['amount'] = this.amount; - if (this.type != null) data['type'] = this.type; - return data; - } + Map toJson() => _$ApplePayItemToJson(this); } diff --git a/lib/src/apple_pay_payment_request.g.dart b/lib/src/apple_pay_payment_request.g.dart new file mode 100644 index 00000000..a475a64b --- /dev/null +++ b/lib/src/apple_pay_payment_request.g.dart @@ -0,0 +1,99 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'apple_pay_payment_request.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Map _$ApplePayPaymentOptionsToJson( + ApplePayPaymentOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull( + 'requiredBillingAddressFields', + instance.requiredBillingAddressFields + ?.map((e) => _$RequiredBillingAddressFieldsEnumMap[e]) + ?.toList()); + writeNotNull( + 'requiredShippingAddressFields', + instance.requiredShippingAddressFields + ?.map((e) => _$RequiredShippingAddressFieldsEnumMap[e]) + ?.toList()); + writeNotNull('shippingMethod', + instance.shippingMethod?.map((e) => e?.toJson())?.toList()); + writeNotNull('currencyCode', instance.currencyCode); + writeNotNull('countryCode', instance.countryCode); + writeNotNull('shippingType', _$ShippingTypeEnumMap[instance.shippingType]); + writeNotNull('items', instance.items?.map((e) => e?.toJson())?.toList()); + return val; +} + +const _$RequiredBillingAddressFieldsEnumMap = { + RequiredBillingAddressFields.all: 'all', + RequiredBillingAddressFields.name: 'name', + RequiredBillingAddressFields.email: 'email', + RequiredBillingAddressFields.phone: 'phone', + RequiredBillingAddressFields.postal_address: 'postal_address', +}; + +const _$RequiredShippingAddressFieldsEnumMap = { + RequiredShippingAddressFields.all: 'all', + RequiredShippingAddressFields.name: 'name', + RequiredShippingAddressFields.email: 'email', + RequiredShippingAddressFields.phone: 'phone', + RequiredShippingAddressFields.postal_address: 'postal_address', +}; + +const _$ShippingTypeEnumMap = { + ShippingType.shipping: 'shipping', + ShippingType.delivery: 'delivery', + ShippingType.store_pickup: 'store_pickup', + ShippingType.service_pickup: 'service_pickup', +}; + +ShippingMethod _$ShippingMethodFromJson(Map json) { + return ShippingMethod( + amount: json['amount'] as String, + detail: json['detail'] as String, + id: json['id'] as String, + label: json['label'] as String, + ); +} + +Map _$ShippingMethodToJson(ShippingMethod instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('amount', instance.amount); + writeNotNull('detail', instance.detail); + writeNotNull('id', instance.id); + writeNotNull('label', instance.label); + return val; +} + +Map _$ApplePayItemToJson(ApplePayItem instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('label', instance.label); + writeNotNull('amount', instance.amount); + writeNotNull('type', instance.type); + return val; +} diff --git a/lib/src/card_form_payment_request.dart b/lib/src/card_form_payment_request.dart deleted file mode 100644 index 508708a2..00000000 --- a/lib/src/card_form_payment_request.dart +++ /dev/null @@ -1,80 +0,0 @@ -class CardFormPaymentRequest { - PrefilledInformation prefilledInformation; - String requiredBillingAddressFields; - - CardFormPaymentRequest({this.prefilledInformation, this.requiredBillingAddressFields}); - - factory CardFormPaymentRequest.fromJson(Map json) { - return CardFormPaymentRequest( - prefilledInformation: - json['prefilledInformation'] != null ? PrefilledInformation.fromJson(json['prefilledInformation']) : null, - requiredBillingAddressFields: json['requiredBillingAddressFields'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.requiredBillingAddressFields != null) if (this.requiredBillingAddressFields != null) - data['requiredBillingAddressFields'] = this.requiredBillingAddressFields; - if (this.prefilledInformation != null) { - data['prefilledInformation'] = this.prefilledInformation.toJson(); - } - return data; - } -} - -class PrefilledInformation { - BillingAddress billingAddress; - - PrefilledInformation({this.billingAddress}); - - factory PrefilledInformation.fromJson(Map json) { - return PrefilledInformation( - billingAddress: json['billingAddress'] != null ? BillingAddress.fromJson(json['billingAddress']) : null, - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.billingAddress != null) { - data['billingAddress'] = this.billingAddress.toJson(); - } - return data; - } -} - -class BillingAddress { - String city; - String country; - String line1; - String line2; - String name; - String postalCode; - String state; - - BillingAddress({this.city, this.country, this.line1, this.line2, this.name, this.postalCode, this.state}); - - factory BillingAddress.fromJson(Map json) { - return BillingAddress( - city: json['city'], - country: json['country'], - line1: json['line1'], - line2: json['line2'], - name: json['name'], - postalCode: json['postalCode'], - state: json['state'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.city != null) data['city'] = this.city; - if (this.country != null) data['country'] = this.country; - if (this.line1 != null) data['line1'] = this.line1; - if (this.line2 != null) data['line2'] = this.line2; - if (this.name != null) data['name'] = this.name; - if (this.postalCode != null) data['postalCode'] = this.postalCode; - if (this.state != null) data['state'] = this.state; - return data; - } -} diff --git a/lib/src/ios/contact_details.dart b/lib/src/ios/contact_details.dart new file mode 100644 index 00000000..c95ebdef --- /dev/null +++ b/lib/src/ios/contact_details.dart @@ -0,0 +1,34 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'contact_details.g.dart'; + +@JsonSerializable() +class ContactDetails { + final String name; + final String phoneNumber; + final String emailAddress; + final String supplementarySubLocality; + final String street; + final String city; + final String state; + final String country; + @JsonKey(name: 'ISOCountryCode') + final String isoCountryCode; + final String postalCode; + + const ContactDetails({ + this.name, + this.phoneNumber, + this.emailAddress, + this.supplementarySubLocality, + this.street, + this.city, + this.state, + this.country, + this.isoCountryCode, + this.postalCode, + }); + + factory ContactDetails.fromJson(Map json) => _$ContactDetailsFromJson(json); + Map toJson() => _$ContactDetailsToJson(this); +} diff --git a/lib/src/ios/contact_details.g.dart b/lib/src/ios/contact_details.g.dart new file mode 100644 index 00000000..7fcd8970 --- /dev/null +++ b/lib/src/ios/contact_details.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'contact_details.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ContactDetails _$ContactDetailsFromJson(Map json) { + return ContactDetails( + name: json['name'] as String, + phoneNumber: json['phoneNumber'] as String, + emailAddress: json['emailAddress'] as String, + supplementarySubLocality: json['supplementarySubLocality'] as String, + street: json['street'] as String, + city: json['city'] as String, + state: json['state'] as String, + country: json['country'] as String, + isoCountryCode: json['ISOCountryCode'] as String, + postalCode: json['postalCode'] as String, + ); +} + +Map _$ContactDetailsToJson(ContactDetails instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('name', instance.name); + writeNotNull('phoneNumber', instance.phoneNumber); + writeNotNull('emailAddress', instance.emailAddress); + writeNotNull('supplementarySubLocality', instance.supplementarySubLocality); + writeNotNull('street', instance.street); + writeNotNull('city', instance.city); + writeNotNull('state', instance.state); + writeNotNull('country', instance.country); + writeNotNull('ISOCountryCode', instance.isoCountryCode); + writeNotNull('postalCode', instance.postalCode); + return val; +} diff --git a/lib/src/model/ach_credit_transfer.dart b/lib/src/model/ach_credit_transfer.dart new file mode 100644 index 00000000..daf396f7 --- /dev/null +++ b/lib/src/model/ach_credit_transfer.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'ach_credit_transfer.g.dart'; + +@JsonSerializable() +class AchCreditTransfer { + String accountNumber; + String bankName; + String fingerprint; + String routingNumber; + String swiftCode; + + AchCreditTransfer({ + this.accountNumber, + this.bankName, + this.fingerprint, + this.routingNumber, + this.swiftCode, + }); + + factory AchCreditTransfer.fromJson(Map json) => _$AchCreditTransferFromJson(json); + Map toJson() => _$AchCreditTransferToJson(this); +} diff --git a/lib/src/model/ach_credit_transfer.g.dart b/lib/src/model/ach_credit_transfer.g.dart new file mode 100644 index 00000000..ca08773b --- /dev/null +++ b/lib/src/model/ach_credit_transfer.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ach_credit_transfer.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AchCreditTransfer _$AchCreditTransferFromJson(Map json) { + return AchCreditTransfer( + accountNumber: json['accountNumber'] as String, + bankName: json['bankName'] as String, + fingerprint: json['fingerprint'] as String, + routingNumber: json['routingNumber'] as String, + swiftCode: json['swiftCode'] as String, + ); +} + +Map _$AchCreditTransferToJson(AchCreditTransfer instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('accountNumber', instance.accountNumber); + writeNotNull('bankName', instance.bankName); + writeNotNull('fingerprint', instance.fingerprint); + writeNotNull('routingNumber', instance.routingNumber); + writeNotNull('swiftCode', instance.swiftCode); + return val; +} diff --git a/lib/src/model/address.dart b/lib/src/model/address.dart new file mode 100644 index 00000000..35f72218 --- /dev/null +++ b/lib/src/model/address.dart @@ -0,0 +1,27 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'address.g.dart'; + +@JsonSerializable() +class Address { + final String city; + final String country; + final String line1; + final String line2; + final String name; + final String postalCode; + final String state; + + const Address({ + this.city, + this.country, + this.line1, + this.line2, + this.name, + this.postalCode, + this.state, + }); + + factory Address.fromJson(Map json) => _$AddressFromJson(json); + Map toJson() => _$AddressToJson(this); +} diff --git a/lib/src/model/address.g.dart b/lib/src/model/address.g.dart new file mode 100644 index 00000000..fc00f367 --- /dev/null +++ b/lib/src/model/address.g.dart @@ -0,0 +1,38 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'address.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Address _$AddressFromJson(Map json) { + return Address( + city: json['city'] as String, + country: json['country'] as String, + line1: json['line1'] as String, + line2: json['line2'] as String, + name: json['name'] as String, + postalCode: json['postalCode'] as String, + state: json['state'] as String, + ); +} + +Map _$AddressToJson(Address instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('city', instance.city); + writeNotNull('country', instance.country); + writeNotNull('line1', instance.line1); + writeNotNull('line2', instance.line2); + writeNotNull('name', instance.name); + writeNotNull('postalCode', instance.postalCode); + writeNotNull('state', instance.state); + return val; +} diff --git a/lib/src/model/all.dart b/lib/src/model/all.dart new file mode 100644 index 00000000..0b87c7ad --- /dev/null +++ b/lib/src/model/all.dart @@ -0,0 +1,18 @@ +export 'ach_credit_transfer.dart'; +export 'all.dart'; +export 'bank_account.dart'; +export 'address.dart'; +export 'billing_details.dart'; +export 'card_form_payment_request.dart'; +export 'credit_card.dart'; +export 'owner.dart'; +export 'payment_intent.dart'; +export 'payment_intent_result.dart'; +export 'payment_method.dart'; +export 'payment_method_request.dart'; +export 'prefilled_information.dart'; +export 'receiver.dart'; +export 'setup_intent_result.dart'; +export 'source.dart'; +export 'source_params.dart'; +export 'token.dart'; diff --git a/lib/src/model/bank_account.dart b/lib/src/model/bank_account.dart new file mode 100644 index 00000000..f8c68217 --- /dev/null +++ b/lib/src/model/bank_account.dart @@ -0,0 +1,36 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'bank_account.g.dart'; + +enum BankAccountAccountHolderType { + company, + individual, +} + +@JsonSerializable() +class BankAccount { + final String accountHolderName; + final BankAccountAccountHolderType accountHolderType; + final String accountNumber; + final String bankName; + final String countryCode; + final String currency; + final String fingerprint; + final String last4; + final String routingNumber; + + const BankAccount({ + this.accountHolderName, + this.accountHolderType, + this.accountNumber, + this.bankName, + this.countryCode, + this.currency, + this.fingerprint, + this.last4, + this.routingNumber, + }); + + factory BankAccount.fromJson(Map json) => _$BankAccountFromJson(json); + Map toJson() => _$BankAccountToJson(this); +} diff --git a/lib/src/model/bank_account.g.dart b/lib/src/model/bank_account.g.dart new file mode 100644 index 00000000..9f9a15fd --- /dev/null +++ b/lib/src/model/bank_account.g.dart @@ -0,0 +1,81 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'bank_account.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BankAccount _$BankAccountFromJson(Map json) { + return BankAccount( + accountHolderName: json['accountHolderName'] as String, + accountHolderType: _$enumDecodeNullable( + _$BankAccountAccountHolderTypeEnumMap, json['accountHolderType']), + accountNumber: json['accountNumber'] as String, + bankName: json['bankName'] as String, + countryCode: json['countryCode'] as String, + currency: json['currency'] as String, + fingerprint: json['fingerprint'] as String, + last4: json['last4'] as String, + routingNumber: json['routingNumber'] as String, + ); +} + +Map _$BankAccountToJson(BankAccount instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('accountHolderName', instance.accountHolderName); + writeNotNull('accountHolderType', + _$BankAccountAccountHolderTypeEnumMap[instance.accountHolderType]); + writeNotNull('accountNumber', instance.accountNumber); + writeNotNull('bankName', instance.bankName); + writeNotNull('countryCode', instance.countryCode); + writeNotNull('currency', instance.currency); + writeNotNull('fingerprint', instance.fingerprint); + writeNotNull('last4', instance.last4); + writeNotNull('routingNumber', instance.routingNumber); + return val; +} + +T _$enumDecode( + Map enumValues, + dynamic source, { + T unknownValue, +}) { + if (source == null) { + throw ArgumentError('A value must be provided. Supported values: ' + '${enumValues.values.join(', ')}'); + } + + final value = enumValues.entries + .singleWhere((e) => e.value == source, orElse: () => null) + ?.key; + + if (value == null && unknownValue == null) { + throw ArgumentError('`$source` is not one of the supported values: ' + '${enumValues.values.join(', ')}'); + } + return value ?? unknownValue; +} + +T _$enumDecodeNullable( + Map enumValues, + dynamic source, { + T unknownValue, +}) { + if (source == null) { + return null; + } + return _$enumDecode(enumValues, source, unknownValue: unknownValue); +} + +const _$BankAccountAccountHolderTypeEnumMap = { + BankAccountAccountHolderType.company: 'company', + BankAccountAccountHolderType.individual: 'individual', +}; diff --git a/lib/src/model/billing_details.dart b/lib/src/model/billing_details.dart new file mode 100644 index 00000000..7339dea4 --- /dev/null +++ b/lib/src/model/billing_details.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'address.dart'; + +part 'billing_details.g.dart'; + +@JsonSerializable() +class BillingDetails { + Address address; + String email; + String name; + String phone; + + BillingDetails({ + this.address, + this.email, + this.name, + this.phone, + }); + + factory BillingDetails.fromJson(Map json) => _$BillingDetailsFromJson(json); + Map toJson() => _$BillingDetailsToJson(this); +} diff --git a/lib/src/model/billing_details.g.dart b/lib/src/model/billing_details.g.dart new file mode 100644 index 00000000..ae9153d1 --- /dev/null +++ b/lib/src/model/billing_details.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'billing_details.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BillingDetails _$BillingDetailsFromJson(Map json) { + return BillingDetails( + address: json['address'] == null + ? null + : Address.fromJson(json['address'] as Map), + email: json['email'] as String, + name: json['name'] as String, + phone: json['phone'] as String, + ); +} + +Map _$BillingDetailsToJson(BillingDetails instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('address', instance.address?.toJson()); + writeNotNull('email', instance.email); + writeNotNull('name', instance.name); + writeNotNull('phone', instance.phone); + return val; +} diff --git a/lib/src/model/card_form_payment_request.dart b/lib/src/model/card_form_payment_request.dart new file mode 100644 index 00000000..5684b129 --- /dev/null +++ b/lib/src/model/card_form_payment_request.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'prefilled_information.dart'; + +part 'card_form_payment_request.g.dart'; + +@JsonSerializable() +class CardFormPaymentRequest { + PrefilledInformation prefilledInformation; + String requiredBillingAddressFields; + + CardFormPaymentRequest({ + this.prefilledInformation, + this.requiredBillingAddressFields, + }); + + factory CardFormPaymentRequest.fromJson(Map json) => + _$CardFormPaymentRequestFromJson(json); + Map toJson() => _$CardFormPaymentRequestToJson(this); +} diff --git a/lib/src/model/card_form_payment_request.g.dart b/lib/src/model/card_form_payment_request.g.dart new file mode 100644 index 00000000..6304a7ac --- /dev/null +++ b/lib/src/model/card_form_payment_request.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'card_form_payment_request.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CardFormPaymentRequest _$CardFormPaymentRequestFromJson(Map json) { + return CardFormPaymentRequest( + prefilledInformation: json['prefilledInformation'] == null + ? null + : PrefilledInformation.fromJson(json['prefilledInformation'] as Map), + requiredBillingAddressFields: + json['requiredBillingAddressFields'] as String, + ); +} + +Map _$CardFormPaymentRequestToJson( + CardFormPaymentRequest instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('prefilledInformation', instance.prefilledInformation?.toJson()); + writeNotNull( + 'requiredBillingAddressFields', instance.requiredBillingAddressFields); + return val; +} diff --git a/lib/src/model/code_verification.dart b/lib/src/model/code_verification.dart new file mode 100644 index 00000000..bf9a35a8 --- /dev/null +++ b/lib/src/model/code_verification.dart @@ -0,0 +1,24 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'code_verification.g.dart'; + +enum CodeVerificationStatus { + pending, + succeeded, + failed, +} + +@JsonSerializable() +class CodeVerification { + final int attemptsRemaining; + final CodeVerificationStatus status; + + const CodeVerification({ + this.attemptsRemaining, + this.status, + }); + + factory CodeVerification.fromJson(Map json) => + _$CodeVerificationFromJson(json); + Map toJson() => _$CodeVerificationToJson(this); +} diff --git a/lib/src/model/code_verification.g.dart b/lib/src/model/code_verification.g.dart new file mode 100644 index 00000000..a4137bde --- /dev/null +++ b/lib/src/model/code_verification.g.dart @@ -0,0 +1,67 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'code_verification.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CodeVerification _$CodeVerificationFromJson(Map json) { + return CodeVerification( + attemptsRemaining: json['attemptsRemaining'] as int, + status: + _$enumDecodeNullable(_$CodeVerificationStatusEnumMap, json['status']), + ); +} + +Map _$CodeVerificationToJson(CodeVerification instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('attemptsRemaining', instance.attemptsRemaining); + writeNotNull('status', _$CodeVerificationStatusEnumMap[instance.status]); + return val; +} + +T _$enumDecode( + Map enumValues, + dynamic source, { + T unknownValue, +}) { + if (source == null) { + throw ArgumentError('A value must be provided. Supported values: ' + '${enumValues.values.join(', ')}'); + } + + final value = enumValues.entries + .singleWhere((e) => e.value == source, orElse: () => null) + ?.key; + + if (value == null && unknownValue == null) { + throw ArgumentError('`$source` is not one of the supported values: ' + '${enumValues.values.join(', ')}'); + } + return value ?? unknownValue; +} + +T _$enumDecodeNullable( + Map enumValues, + dynamic source, { + T unknownValue, +}) { + if (source == null) { + return null; + } + return _$enumDecode(enumValues, source, unknownValue: unknownValue); +} + +const _$CodeVerificationStatusEnumMap = { + CodeVerificationStatus.pending: 'pending', + CodeVerificationStatus.succeeded: 'succeeded', + CodeVerificationStatus.failed: 'failed', +}; diff --git a/lib/src/model/credit_card.dart b/lib/src/model/credit_card.dart new file mode 100644 index 00000000..c7304e3a --- /dev/null +++ b/lib/src/model/credit_card.dart @@ -0,0 +1,47 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'credit_card.g.dart'; + +@JsonSerializable() +class CreditCard { + String addressCity; + String addressCountry; + String addressLine1; + String addressLine2; + String addressState; + String addressZip; + String brand; + String cardId; + String country; + int expMonth; + int expYear; + String funding; + String last4; + String name; + String number; + String cvc; + String token; + + CreditCard({ + this.addressCity, + this.addressCountry, + this.addressLine1, + this.addressLine2, + this.addressState, + this.addressZip, + this.brand, + this.cardId, + this.country, + this.expMonth, + this.expYear, + this.number, + this.token, + this.cvc, + this.funding, + this.last4, + this.name, + }); + + factory CreditCard.fromJson(Map json) => _$CreditCardFromJson(json); + Map toJson() => _$CreditCardToJson(this); +} diff --git a/lib/src/model/credit_card.g.dart b/lib/src/model/credit_card.g.dart new file mode 100644 index 00000000..dcde584d --- /dev/null +++ b/lib/src/model/credit_card.g.dart @@ -0,0 +1,58 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'credit_card.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CreditCard _$CreditCardFromJson(Map json) { + return CreditCard( + addressCity: json['addressCity'] as String, + addressCountry: json['addressCountry'] as String, + addressLine1: json['addressLine1'] as String, + addressLine2: json['addressLine2'] as String, + addressState: json['addressState'] as String, + addressZip: json['addressZip'] as String, + brand: json['brand'] as String, + cardId: json['cardId'] as String, + country: json['country'] as String, + expMonth: json['expMonth'] as int, + expYear: json['expYear'] as int, + number: json['number'] as String, + token: json['token'] as String, + cvc: json['cvc'] as String, + funding: json['funding'] as String, + last4: json['last4'] as String, + name: json['name'] as String, + ); +} + +Map _$CreditCardToJson(CreditCard instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('addressCity', instance.addressCity); + writeNotNull('addressCountry', instance.addressCountry); + writeNotNull('addressLine1', instance.addressLine1); + writeNotNull('addressLine2', instance.addressLine2); + writeNotNull('addressState', instance.addressState); + writeNotNull('addressZip', instance.addressZip); + writeNotNull('brand', instance.brand); + writeNotNull('cardId', instance.cardId); + writeNotNull('country', instance.country); + writeNotNull('expMonth', instance.expMonth); + writeNotNull('expYear', instance.expYear); + writeNotNull('funding', instance.funding); + writeNotNull('last4', instance.last4); + writeNotNull('name', instance.name); + writeNotNull('number', instance.number); + writeNotNull('cvc', instance.cvc); + writeNotNull('token', instance.token); + return val; +} diff --git a/lib/src/model/customer.dart b/lib/src/model/customer.dart new file mode 100644 index 00000000..d1dd29c8 --- /dev/null +++ b/lib/src/model/customer.dart @@ -0,0 +1,29 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'address.dart'; + +part 'customer.g.dart'; + +@JsonSerializable() +class Customer { + final String id; + final Address address; + final String description; + final String email; + final Map metadata; + final String name; + final String phone; + + const Customer({ + this.id, + this.address, + this.description, + this.email, + this.metadata, + this.name, + this.phone, + }); + + factory Customer.fromJson(Map json) => _$CustomerFromJson(json); + Map toJson() => _$CustomerToJson(this); +} diff --git a/lib/src/model/customer.g.dart b/lib/src/model/customer.g.dart new file mode 100644 index 00000000..91475f52 --- /dev/null +++ b/lib/src/model/customer.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'customer.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Customer _$CustomerFromJson(Map json) { + return Customer( + id: json['id'] as String, + address: json['address'] == null + ? null + : Address.fromJson(json['address'] as Map), + description: json['description'] as String, + email: json['email'] as String, + metadata: (json['metadata'] as Map)?.map( + (k, e) => MapEntry(k as String, e), + ), + name: json['name'] as String, + phone: json['phone'] as String, + ); +} + +Map _$CustomerToJson(Customer instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('id', instance.id); + writeNotNull('address', instance.address?.toJson()); + writeNotNull('description', instance.description); + writeNotNull('email', instance.email); + writeNotNull('metadata', instance.metadata); + writeNotNull('name', instance.name); + writeNotNull('phone', instance.phone); + return val; +} diff --git a/lib/src/model/owner.dart b/lib/src/model/owner.dart new file mode 100644 index 00000000..2994e9c6 --- /dev/null +++ b/lib/src/model/owner.dart @@ -0,0 +1,31 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'address.dart'; + +part 'owner.g.dart'; + +@JsonSerializable() +class Owner { + final Address address; + final String email; + final String name; + final String phone; + final Address verifiedAddress; + final String verifiedEmail; + final String verifiedName; + final String verifiedPhone; + + const Owner({ + this.address, + this.email, + this.name, + this.phone, + this.verifiedAddress, + this.verifiedEmail, + this.verifiedName, + this.verifiedPhone, + }); + + factory Owner.fromJson(Map json) => _$OwnerFromJson(json); + Map toJson() => _$OwnerToJson(this); +} diff --git a/lib/src/model/owner.g.dart b/lib/src/model/owner.g.dart new file mode 100644 index 00000000..172add26 --- /dev/null +++ b/lib/src/model/owner.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'owner.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Owner _$OwnerFromJson(Map json) { + return Owner( + address: json['address'] == null + ? null + : Address.fromJson(json['address'] as Map), + email: json['email'] as String, + name: json['name'] as String, + phone: json['phone'] as String, + verifiedAddress: json['verifiedAddress'] == null + ? null + : Address.fromJson(json['verifiedAddress'] as Map), + verifiedEmail: json['verifiedEmail'] as String, + verifiedName: json['verifiedName'] as String, + verifiedPhone: json['verifiedPhone'] as String, + ); +} + +Map _$OwnerToJson(Owner instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('address', instance.address?.toJson()); + writeNotNull('email', instance.email); + writeNotNull('name', instance.name); + writeNotNull('phone', instance.phone); + writeNotNull('verifiedAddress', instance.verifiedAddress?.toJson()); + writeNotNull('verifiedEmail', instance.verifiedEmail); + writeNotNull('verifiedName', instance.verifiedName); + writeNotNull('verifiedPhone', instance.verifiedPhone); + return val; +} diff --git a/lib/src/model/payment_intent.dart b/lib/src/model/payment_intent.dart new file mode 100644 index 00000000..fe4034c9 --- /dev/null +++ b/lib/src/model/payment_intent.dart @@ -0,0 +1,26 @@ +import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'payment_method_request.dart'; + +part 'payment_intent.g.dart'; + +@JsonSerializable(explicitToJson: true) +class PaymentIntent { + PaymentMethodRequest paymentMethod; + String paymentMethodId; + String returnURL; + String clientSecret; + final Map metadata; + + PaymentIntent({ + this.paymentMethod, + this.paymentMethodId, + this.returnURL, + @required this.clientSecret, + this.metadata, + }); + + factory PaymentIntent.fromJson(Map json) => _$PaymentIntentFromJson(json); + Map toJson() => _$PaymentIntentToJson(this); +} diff --git a/lib/src/model/payment_intent.g.dart b/lib/src/model/payment_intent.g.dart new file mode 100644 index 00000000..b9dd0fbf --- /dev/null +++ b/lib/src/model/payment_intent.g.dart @@ -0,0 +1,38 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'payment_intent.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PaymentIntent _$PaymentIntentFromJson(Map json) { + return PaymentIntent( + paymentMethod: json['paymentMethod'] == null + ? null + : PaymentMethodRequest.fromJson(json['paymentMethod'] as Map), + paymentMethodId: json['paymentMethodId'] as String, + returnURL: json['returnURL'] as String, + clientSecret: json['clientSecret'] as String, + metadata: (json['metadata'] as Map)?.map( + (k, e) => MapEntry(k as String, e), + ), + ); +} + +Map _$PaymentIntentToJson(PaymentIntent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('paymentMethod', instance.paymentMethod?.toJson()); + writeNotNull('paymentMethodId', instance.paymentMethodId); + writeNotNull('returnURL', instance.returnURL); + writeNotNull('clientSecret', instance.clientSecret); + writeNotNull('metadata', instance.metadata); + return val; +} diff --git a/lib/src/model/payment_intent_result.dart b/lib/src/model/payment_intent_result.dart new file mode 100644 index 00000000..eb379cc7 --- /dev/null +++ b/lib/src/model/payment_intent_result.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'payment_intent_result.g.dart'; + +@JsonSerializable() +class PaymentIntentResult { + final String status; + final String paymentIntentId; + final String paymentMethodId; + + const PaymentIntentResult({ + this.status, + this.paymentIntentId, + this.paymentMethodId, + }); + + factory PaymentIntentResult.fromJson(Map json) => + _$PaymentIntentResultFromJson(json); + Map toJson() => _$PaymentIntentResultToJson(this); +} diff --git a/lib/src/model/payment_intent_result.g.dart b/lib/src/model/payment_intent_result.g.dart new file mode 100644 index 00000000..4ab32896 --- /dev/null +++ b/lib/src/model/payment_intent_result.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'payment_intent_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PaymentIntentResult _$PaymentIntentResultFromJson(Map json) { + return PaymentIntentResult( + status: json['status'] as String, + paymentIntentId: json['paymentIntentId'] as String, + paymentMethodId: json['paymentMethodId'] as String, + ); +} + +Map _$PaymentIntentResultToJson(PaymentIntentResult instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('status', instance.status); + writeNotNull('paymentIntentId', instance.paymentIntentId); + writeNotNull('paymentMethodId', instance.paymentMethodId); + return val; +} diff --git a/lib/src/model/payment_method.dart b/lib/src/model/payment_method.dart new file mode 100644 index 00000000..162e7368 --- /dev/null +++ b/lib/src/model/payment_method.dart @@ -0,0 +1,30 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'billing_details.dart'; +import 'credit_card.dart'; + +part 'payment_method.g.dart'; + +@JsonSerializable(anyMap: true) +class PaymentMethod { + BillingDetails billingDetails; + CreditCard card; + num created; + String customerId; + String id; + bool livemode; + String type; + + PaymentMethod({ + this.billingDetails, + this.card, + this.created, + this.customerId, + this.id, + this.livemode, + this.type, + }); + + factory PaymentMethod.fromJson(Map json) => _$PaymentMethodFromJson(json); + Map toJson() => _$PaymentMethodToJson(this); +} diff --git a/lib/src/model/payment_method.g.dart b/lib/src/model/payment_method.g.dart new file mode 100644 index 00000000..10dbd88a --- /dev/null +++ b/lib/src/model/payment_method.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'payment_method.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PaymentMethod _$PaymentMethodFromJson(Map json) { + return PaymentMethod( + billingDetails: json['billingDetails'] == null + ? null + : BillingDetails.fromJson(json['billingDetails'] as Map), + card: + json['card'] == null ? null : CreditCard.fromJson(json['card'] as Map), + created: json['created'] as num, + customerId: json['customerId'] as String, + id: json['id'] as String, + livemode: json['livemode'] as bool, + type: json['type'] as String, + ); +} + +Map _$PaymentMethodToJson(PaymentMethod instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('billingDetails', instance.billingDetails?.toJson()); + writeNotNull('card', instance.card?.toJson()); + writeNotNull('created', instance.created); + writeNotNull('customerId', instance.customerId); + writeNotNull('id', instance.id); + writeNotNull('livemode', instance.livemode); + writeNotNull('type', instance.type); + return val; +} diff --git a/lib/src/model/payment_method_request.dart b/lib/src/model/payment_method_request.dart new file mode 100644 index 00000000..3584cde0 --- /dev/null +++ b/lib/src/model/payment_method_request.dart @@ -0,0 +1,27 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'address.dart'; +import 'credit_card.dart'; +import 'token.dart'; + +part 'payment_method_request.g.dart'; + +@JsonSerializable(explicitToJson: true) +class PaymentMethodRequest { + final Address billingAddress; + final CreditCard card; + final Token token; + final Map metadata; + + + const PaymentMethodRequest({ + this.billingAddress, + this.card, + this.token, + this.metadata, + }) : assert(card != null || token != null); + + factory PaymentMethodRequest.fromJson(Map json) => + _$PaymentMethodRequestFromJson(json); + Map toJson() => _$PaymentMethodRequestToJson(this); +} diff --git a/lib/src/model/payment_method_request.g.dart b/lib/src/model/payment_method_request.g.dart new file mode 100644 index 00000000..e1efd41e --- /dev/null +++ b/lib/src/model/payment_method_request.g.dart @@ -0,0 +1,38 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'payment_method_request.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PaymentMethodRequest _$PaymentMethodRequestFromJson(Map json) { + return PaymentMethodRequest( + billingAddress: json['billingAddress'] == null + ? null + : Address.fromJson(json['billingAddress'] as Map), + card: + json['card'] == null ? null : CreditCard.fromJson(json['card'] as Map), + token: json['token'] == null ? null : Token.fromJson(json['token'] as Map), + metadata: (json['metadata'] as Map)?.map( + (k, e) => MapEntry(k as String, e as String), + ), + ); +} + +Map _$PaymentMethodRequestToJson( + PaymentMethodRequest instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('billingAddress', instance.billingAddress?.toJson()); + writeNotNull('card', instance.card?.toJson()); + writeNotNull('token', instance.token?.toJson()); + writeNotNull('metadata', instance.metadata); + return val; +} diff --git a/lib/src/model/prefilled_information.dart b/lib/src/model/prefilled_information.dart new file mode 100644 index 00000000..0b6a3d1a --- /dev/null +++ b/lib/src/model/prefilled_information.dart @@ -0,0 +1,18 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'address.dart'; + +part 'prefilled_information.g.dart'; + +@JsonSerializable() +class PrefilledInformation { + final Address billingAddress; + + const PrefilledInformation({ + this.billingAddress, + }); + + factory PrefilledInformation.fromJson(Map json) => + _$PrefilledInformationFromJson(json); + Map toJson() => _$PrefilledInformationToJson(this); +} diff --git a/lib/src/model/prefilled_information.g.dart b/lib/src/model/prefilled_information.g.dart new file mode 100644 index 00000000..022fbaa6 --- /dev/null +++ b/lib/src/model/prefilled_information.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'prefilled_information.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PrefilledInformation _$PrefilledInformationFromJson(Map json) { + return PrefilledInformation( + billingAddress: json['billingAddress'] == null + ? null + : Address.fromJson(json['billingAddress'] as Map), + ); +} + +Map _$PrefilledInformationToJson( + PrefilledInformation instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('billingAddress', instance.billingAddress?.toJson()); + return val; +} diff --git a/lib/src/model/receiver.dart b/lib/src/model/receiver.dart new file mode 100644 index 00000000..4728e42e --- /dev/null +++ b/lib/src/model/receiver.dart @@ -0,0 +1,25 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'receiver.g.dart'; + +@JsonSerializable() +class Receiver { + final String address; + final int amountCharged; + final int amountReceived; + final int amountReturned; + final String refundAttributesMethod; + final String refundAttributesStatus; + + const Receiver({ + this.address, + this.amountCharged, + this.amountReceived, + this.amountReturned, + this.refundAttributesMethod, + this.refundAttributesStatus, + }); + + factory Receiver.fromJson(Map json) => _$ReceiverFromJson(json); + Map toJson() => _$ReceiverToJson(this); +} diff --git a/lib/src/model/receiver.g.dart b/lib/src/model/receiver.g.dart new file mode 100644 index 00000000..15f012d9 --- /dev/null +++ b/lib/src/model/receiver.g.dart @@ -0,0 +1,36 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'receiver.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Receiver _$ReceiverFromJson(Map json) { + return Receiver( + address: json['address'] as String, + amountCharged: json['amountCharged'] as int, + amountReceived: json['amountReceived'] as int, + amountReturned: json['amountReturned'] as int, + refundAttributesMethod: json['refundAttributesMethod'] as String, + refundAttributesStatus: json['refundAttributesStatus'] as String, + ); +} + +Map _$ReceiverToJson(Receiver instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('address', instance.address); + writeNotNull('amountCharged', instance.amountCharged); + writeNotNull('amountReceived', instance.amountReceived); + writeNotNull('amountReturned', instance.amountReturned); + writeNotNull('refundAttributesMethod', instance.refundAttributesMethod); + writeNotNull('refundAttributesStatus', instance.refundAttributesStatus); + return val; +} diff --git a/lib/src/model/setup_intent_result.dart b/lib/src/model/setup_intent_result.dart new file mode 100644 index 00000000..9beb55e7 --- /dev/null +++ b/lib/src/model/setup_intent_result.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'setup_intent_result.g.dart'; + +@JsonSerializable() +class SetupIntentResult { + final String status; + final String paymentIntentId; + final String setupIntentId; + + const SetupIntentResult({ + this.status, + this.setupIntentId, + this.paymentIntentId, + }); + + factory SetupIntentResult.fromJson(Map json) => + _$SetupIntentResultFromJson(json); + Map toJson() => _$SetupIntentResultToJson(this); +} diff --git a/lib/src/model/setup_intent_result.g.dart b/lib/src/model/setup_intent_result.g.dart new file mode 100644 index 00000000..aeb9fc1c --- /dev/null +++ b/lib/src/model/setup_intent_result.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'setup_intent_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SetupIntentResult _$SetupIntentResultFromJson(Map json) { + return SetupIntentResult( + status: json['status'] as String, + setupIntentId: json['setupIntentId'] as String, + paymentIntentId: json['paymentIntentId'] as String, + ); +} + +Map _$SetupIntentResultToJson(SetupIntentResult instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('status', instance.status); + writeNotNull('paymentIntentId', instance.paymentIntentId); + writeNotNull('setupIntentId', instance.setupIntentId); + return val; +} diff --git a/lib/src/model/source.dart b/lib/src/model/source.dart new file mode 100644 index 00000000..a19d8c2e --- /dev/null +++ b/lib/src/model/source.dart @@ -0,0 +1,75 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'ach_credit_transfer.dart'; +import 'code_verification.dart'; +import 'owner.dart'; +import 'receiver.dart'; + +part 'source.g.dart'; + +enum SourceFlow { + redirect, + receiver, + codeVerification, + none, + unknown, +} + +enum SourceStatus { + canceled, + chargeable, + consumed, + failed, + pending, + unknown, +} + +enum SourceUsage { + reusable, + singleUse, + unknown, +} + +@JsonSerializable() +class Source { + final String object; + final Receiver receiver; + final AchCreditTransfer achCreditTransfer; + final num amount; + final String clientSecret; + final CodeVerification codeVerification; + final num created; + final String currency; + final SourceFlow flow; + final String sourceId; + final bool livemode; + final Map metadata; + final Owner owner; + final String statementDescriptor; + final SourceStatus status; + final String type; + final SourceUsage usage; + + const Source({ + this.object, + this.receiver, + this.achCreditTransfer, + this.amount, + this.clientSecret, + this.codeVerification, + this.created, + this.currency, + this.flow, + this.sourceId, + this.livemode, + this.metadata, + this.owner, + this.statementDescriptor, + this.status, + this.type, + this.usage, + }); + + factory Source.fromJson(Map json) => _$SourceFromJson(json); + Map toJson() => _$SourceToJson(this); +} diff --git a/lib/src/model/source.g.dart b/lib/src/model/source.g.dart new file mode 100644 index 00000000..6ab98c75 --- /dev/null +++ b/lib/src/model/source.g.dart @@ -0,0 +1,119 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'source.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Source _$SourceFromJson(Map json) { + return Source( + object: json['object'] as String, + receiver: json['receiver'] == null + ? null + : Receiver.fromJson(json['receiver'] as Map), + achCreditTransfer: json['achCreditTransfer'] == null + ? null + : AchCreditTransfer.fromJson(json['achCreditTransfer'] as Map), + amount: json['amount'] as num, + clientSecret: json['clientSecret'] as String, + codeVerification: json['codeVerification'] == null + ? null + : CodeVerification.fromJson(json['codeVerification'] as Map), + created: json['created'] as num, + currency: json['currency'] as String, + flow: _$enumDecodeNullable(_$SourceFlowEnumMap, json['flow']), + sourceId: json['sourceId'] as String, + livemode: json['livemode'] as bool, + metadata: json['metadata'] as Map, + owner: json['owner'] == null ? null : Owner.fromJson(json['owner'] as Map), + statementDescriptor: json['statementDescriptor'] as String, + status: _$enumDecodeNullable(_$SourceStatusEnumMap, json['status']), + type: json['type'] as String, + usage: _$enumDecodeNullable(_$SourceUsageEnumMap, json['usage']), + ); +} + +Map _$SourceToJson(Source instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('object', instance.object); + writeNotNull('receiver', instance.receiver?.toJson()); + writeNotNull('achCreditTransfer', instance.achCreditTransfer?.toJson()); + writeNotNull('amount', instance.amount); + writeNotNull('clientSecret', instance.clientSecret); + writeNotNull('codeVerification', instance.codeVerification?.toJson()); + writeNotNull('created', instance.created); + writeNotNull('currency', instance.currency); + writeNotNull('flow', _$SourceFlowEnumMap[instance.flow]); + writeNotNull('sourceId', instance.sourceId); + writeNotNull('livemode', instance.livemode); + writeNotNull('metadata', instance.metadata); + writeNotNull('owner', instance.owner?.toJson()); + writeNotNull('statementDescriptor', instance.statementDescriptor); + writeNotNull('status', _$SourceStatusEnumMap[instance.status]); + writeNotNull('type', instance.type); + writeNotNull('usage', _$SourceUsageEnumMap[instance.usage]); + return val; +} + +T _$enumDecode( + Map enumValues, + dynamic source, { + T unknownValue, +}) { + if (source == null) { + throw ArgumentError('A value must be provided. Supported values: ' + '${enumValues.values.join(', ')}'); + } + + final value = enumValues.entries + .singleWhere((e) => e.value == source, orElse: () => null) + ?.key; + + if (value == null && unknownValue == null) { + throw ArgumentError('`$source` is not one of the supported values: ' + '${enumValues.values.join(', ')}'); + } + return value ?? unknownValue; +} + +T _$enumDecodeNullable( + Map enumValues, + dynamic source, { + T unknownValue, +}) { + if (source == null) { + return null; + } + return _$enumDecode(enumValues, source, unknownValue: unknownValue); +} + +const _$SourceFlowEnumMap = { + SourceFlow.redirect: 'redirect', + SourceFlow.receiver: 'receiver', + SourceFlow.codeVerification: 'codeVerification', + SourceFlow.none: 'none', + SourceFlow.unknown: 'unknown', +}; + +const _$SourceStatusEnumMap = { + SourceStatus.canceled: 'canceled', + SourceStatus.chargeable: 'chargeable', + SourceStatus.consumed: 'consumed', + SourceStatus.failed: 'failed', + SourceStatus.pending: 'pending', + SourceStatus.unknown: 'unknown', +}; + +const _$SourceUsageEnumMap = { + SourceUsage.reusable: 'reusable', + SourceUsage.singleUse: 'singleUse', + SourceUsage.unknown: 'unknown', +}; diff --git a/lib/src/model/source_params.dart b/lib/src/model/source_params.dart new file mode 100644 index 00000000..d5394489 --- /dev/null +++ b/lib/src/model/source_params.dart @@ -0,0 +1,34 @@ +import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'credit_card.dart'; + +part 'source_params.g.dart'; + +@JsonSerializable() +class SourceParams { + final int amount; + final String currency; + final String returnURL; + final String type; + final String name; + final String statementDescriptor; + final String country; + final String email; + final CreditCard card; + + const SourceParams({ + this.amount, + this.currency, + @required this.returnURL, + @required this.type, + this.name, + this.statementDescriptor, + this.country, + this.email, + this.card, + }); + + factory SourceParams.fromJson(Map json) => _$SourceParamsFromJson(json); + Map toJson() => _$SourceParamsToJson(this); +} diff --git a/lib/src/model/source_params.g.dart b/lib/src/model/source_params.g.dart new file mode 100644 index 00000000..483e7e68 --- /dev/null +++ b/lib/src/model/source_params.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'source_params.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SourceParams _$SourceParamsFromJson(Map json) { + return SourceParams( + amount: json['amount'] as int, + currency: json['currency'] as String, + returnURL: json['returnURL'] as String, + type: json['type'] as String, + name: json['name'] as String, + statementDescriptor: json['statementDescriptor'] as String, + country: json['country'] as String, + email: json['email'] as String, + card: + json['card'] == null ? null : CreditCard.fromJson(json['card'] as Map), + ); +} + +Map _$SourceParamsToJson(SourceParams instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('amount', instance.amount); + writeNotNull('currency', instance.currency); + writeNotNull('returnURL', instance.returnURL); + writeNotNull('type', instance.type); + writeNotNull('name', instance.name); + writeNotNull('statementDescriptor', instance.statementDescriptor); + writeNotNull('country', instance.country); + writeNotNull('email', instance.email); + writeNotNull('card', instance.card?.toJson()); + return val; +} diff --git a/lib/src/model/token.dart b/lib/src/model/token.dart new file mode 100644 index 00000000..dba82b8b --- /dev/null +++ b/lib/src/model/token.dart @@ -0,0 +1,40 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:stripe_payment/src/ios/contact_details.dart'; + +import 'bank_account.dart'; +import 'credit_card.dart'; + +part 'token.g.dart'; + +@JsonSerializable() +class Token { + final BankAccount bankAccount; + final CreditCard card; + final double created; + final bool livemode; + final String tokenId; + final TokenExtra extra; + + Token({ + this.bankAccount, + this.card, + this.created, + this.livemode, + this.tokenId, + this.extra, + }); + + factory Token.fromJson(Map json) => _$TokenFromJson(json); + Map toJson() => _$TokenToJson(this); +} + +@JsonSerializable() +class TokenExtra { + final ContactDetails billingContact; + final ContactDetails shippingContact; + + TokenExtra(this.billingContact, this.shippingContact); + + factory TokenExtra.fromJson(Map json) => _$TokenExtraFromJson(json); + Map toJson() => _$TokenExtraToJson(this); +} diff --git a/lib/src/model/token.g.dart b/lib/src/model/token.g.dart new file mode 100644 index 00000000..01aca795 --- /dev/null +++ b/lib/src/model/token.g.dart @@ -0,0 +1,66 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'token.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Token _$TokenFromJson(Map json) { + return Token( + bankAccount: json['bankAccount'] == null + ? null + : BankAccount.fromJson(json['bankAccount'] as Map), + card: + json['card'] == null ? null : CreditCard.fromJson(json['card'] as Map), + created: (json['created'] as num)?.toDouble(), + livemode: json['livemode'] as bool, + tokenId: json['tokenId'] as String, + extra: json['extra'] == null + ? null + : TokenExtra.fromJson(json['extra'] as Map), + ); +} + +Map _$TokenToJson(Token instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('bankAccount', instance.bankAccount?.toJson()); + writeNotNull('card', instance.card?.toJson()); + writeNotNull('created', instance.created); + writeNotNull('livemode', instance.livemode); + writeNotNull('tokenId', instance.tokenId); + writeNotNull('extra', instance.extra?.toJson()); + return val; +} + +TokenExtra _$TokenExtraFromJson(Map json) { + return TokenExtra( + json['billingContact'] == null + ? null + : ContactDetails.fromJson(json['billingContact'] as Map), + json['shippingContact'] == null + ? null + : ContactDetails.fromJson(json['shippingContact'] as Map), + ); +} + +Map _$TokenExtraToJson(TokenExtra instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('billingContact', instance.billingContact?.toJson()); + writeNotNull('shippingContact', instance.shippingContact?.toJson()); + return val; +} diff --git a/lib/src/payment_intent.dart b/lib/src/payment_intent.dart deleted file mode 100644 index 16c1882d..00000000 --- a/lib/src/payment_intent.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stripe_payment/src/payment_method.dart'; - -class PaymentIntent { - PaymentMethodRequest paymentMethod; - String paymentMethodId; - String returnURL; - String clientSecret; - - PaymentIntent({ - this.paymentMethod, - this.paymentMethodId, - this.returnURL, - @required this.clientSecret, - }); - - Map toJson() { - final Map data = new Map(); - if (this.paymentMethod != null) { - data['paymentMethod'] = this.paymentMethod.toJson(); - } - if (this.paymentMethodId != null) data['paymentMethodId'] = this.paymentMethodId; - if (this.returnURL != null) data['returnURL'] = this.returnURL; - if (this.clientSecret != null) data['clientSecret'] = this.clientSecret; - return data; - } -} - -class PaymentIntentResult { - String status; - String paymentIntentId; - String paymentMethodId; - - PaymentIntentResult({this.status, this.paymentIntentId, this.paymentMethodId}); - - factory PaymentIntentResult.fromJson(Map json) { - return PaymentIntentResult( - status: json['status'], - paymentIntentId: json['paymentIntentId'], - paymentMethodId: json['paymentMethodId'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.paymentIntentId != null) data['paymentIntentId'] = this.paymentIntentId; - if (this.status != null) data['status'] = this.status; - if (this.paymentMethodId != null) data['paymentMethodId'] = this.paymentMethodId; - return data; - } -} - -class SetupIntentResult { - String status; - String paymentIntentId; - String setupIntentId; - - SetupIntentResult({ - this.status, - this.setupIntentId, - this.paymentIntentId, - }); - - factory SetupIntentResult.fromJson(Map json) { - return SetupIntentResult( - status: json['status'], - setupIntentId: json['setupIntentId'], - paymentIntentId: json['paymentIntentId'], - ); - } -} diff --git a/lib/src/payment_method.dart b/lib/src/payment_method.dart deleted file mode 100644 index 5ccccaaf..00000000 --- a/lib/src/payment_method.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:stripe_payment/src/token.dart'; - -import 'card_form_payment_request.dart'; - -class PaymentMethodRequest { - final BillingAddress billingAddress; - final CreditCard card; - final Token token; - final Map metadata; - - PaymentMethodRequest({this.billingAddress, this.card, this.token, this.metadata}) { - assert(card != null || token != null); - } - - Map toJson() { - final Map data = new Map(); - if (this.card != null) { - data['card'] = card.toJson(); - } - if (this.token != null) { - data['token'] = token.toJson(); - } - if (this.billingAddress != null) { - data['billingAddress'] = this.billingAddress.toJson(); - } - if (this.metadata != null) { - if (this.metadata != null) data['metadata'] = this.metadata; - } - return data; - } -} - -class PaymentMethod { - BillingDetails billingDetails; - CreditCard card; - num created; - String customerId; - String id; - bool livemode; - String type; - - PaymentMethod({this.billingDetails, this.card, this.created, this.customerId, this.id, this.livemode, this.type}); - - factory PaymentMethod.fromJson(Map json) { - return PaymentMethod( - billingDetails: json['billingDetails'] != null ? BillingDetails.fromJson(json['billingDetails']) : null, - card: json['card'] != null ? CreditCard.fromJson(json['card']) : null, - created: json['created'], - customerId: json['customerId'], - id: json['id'], - livemode: json['livemode'], - type: json['type'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.created != null) if (this.created != null) data['created'] = this.created; - if (this.customerId != null) data['customerId'] = this.customerId; - if (this.id != null) data['id'] = this.id; - if (this.livemode != null) data['livemode'] = this.livemode; - if (this.type != null) data['type'] = this.type; - if (this.billingDetails != null) { - data['billingDetails'] = this.billingDetails.toJson(); - } - if (this.card != null) { - data['card'] = this.card.toJson(); - } - return data; - } -} - -class BillingDetails { - BillingAddress address; - String email; - String name; - String phone; - - BillingDetails({this.address, this.email, this.name, this.phone}); - - factory BillingDetails.fromJson(Map json) { - return BillingDetails( - address: json['address'] != null ? BillingAddress.fromJson(json['address']) : null, - email: json['email'], - name: json['name'], - phone: json['phone'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.email != null) data['email'] = this.email; - if (this.name != null) data['name'] = this.name; - if (this.phone != null) data['phone'] = this.phone; - if (this.address != null) { - data['address'] = this.address.toJson(); - } - return data; - } -} diff --git a/lib/src/source.dart b/lib/src/source.dart deleted file mode 100644 index 628eb160..00000000 --- a/lib/src/source.dart +++ /dev/null @@ -1,205 +0,0 @@ -import 'package:stripe_payment/src/card_form_payment_request.dart'; - -class Source { - String object; - Receiver receiver; - AchCreditTransfer achCreditTransfer; - num amount; - String clientSecret; - num created; - String currency; - String flow; - String sourceId; - bool livemode; - Map metadata; - Owner owner; - String statementDescriptor; - String status; - String type; - String usage; - - Source( - {this.object, - this.receiver, - this.achCreditTransfer, - this.amount, - this.clientSecret, - this.created, - this.currency, - this.flow, - this.sourceId, - this.livemode, - this.metadata, - this.owner, - this.statementDescriptor, - this.status, - this.type, - this.usage}); - - factory Source.fromJson(Map json) { - return Source( - object: json['object'], - receiver: json['receiver'] != null ? Receiver.fromJson(json['receiver']) : null, - achCreditTransfer: - json['ach_credit_transfer'] != null ? AchCreditTransfer.fromJson(json['ach_credit_transfer']) : null, - amount: json['amount'], - clientSecret: json['client_secret'], - created: json['created'], - currency: json['currency'], - flow: json['flow'], - sourceId: json['sourceId'], - livemode: json['livemode'], - metadata: json['metadata'], - owner: json['owner'] != null ? Owner.fromJson(json['owner']) : null, - statementDescriptor: json['statement_descriptor'], - status: json['status'], - type: json['type'], - usage: json['usage'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.object != null) data['object'] = this.object; - if (this.clientSecret != null) data['client_secret'] = this.clientSecret; - if (this.created != null) data['created'] = this.created; - if (this.currency != null) data['currency'] = this.currency; - if (this.flow != null) data['flow'] = this.flow; - if (this.sourceId != null) data['sourceId'] = this.sourceId; - if (this.livemode != null) data['livemode'] = this.livemode; - if (this.statementDescriptor != null) data['statement_descriptor'] = this.statementDescriptor; - if (this.status != null) data['status'] = this.status; - if (this.type != null) data['type'] = this.type; - if (this.usage != null) data['usage'] = this.usage; - if (this.receiver != null) { - data['receiver'] = this.receiver.toJson(); - } - if (this.achCreditTransfer != null) { - data['ach_credit_transfer'] = this.achCreditTransfer.toJson(); - } - if (this.amount != null) data['amount'] = this.amount; - if (this.metadata != null) { - if (this.metadata != null) data['metadata'] = this.metadata; - } - if (this.owner != null) { - data['owner'] = this.owner.toJson(); - } - return data; - } -} - -class Receiver { - String address; - int amountCharged; - int amountReceived; - int amountReturned; - String refundAttributesMethod; - String refundAttributesStatus; - - Receiver( - {this.address, - this.amountCharged, - this.amountReceived, - this.amountReturned, - this.refundAttributesMethod, - this.refundAttributesStatus}); - - factory Receiver.fromJson(Map json) { - return Receiver( - address: json['address'], - amountCharged: json['amount_charged'], - amountReceived: json['amount_received'], - amountReturned: json['amount_returned'], - refundAttributesMethod: json['refund_attributes_method'], - refundAttributesStatus: json['refund_attributes_status'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.address != null) data['address'] = this.address; - if (this.amountCharged != null) data['amount_charged'] = this.amountCharged; - if (this.amountReceived != null) data['amount_received'] = this.amountReceived; - if (this.amountReturned != null) data['amount_returned'] = this.amountReturned; - if (this.refundAttributesMethod != null) data['refund_attributes_method'] = this.refundAttributesMethod; - if (this.refundAttributesStatus != null) data['refund_attributes_status'] = this.refundAttributesStatus; - return data; - } -} - -class Owner { - BillingAddress address; - String email; - String name; - String phone; - BillingAddress verifiedAddress; - String verifiedEmail; - String verifiedName; - String verifiedPhone; - - Owner( - {this.address, - this.email, - this.name, - this.phone, - this.verifiedAddress, - this.verifiedEmail, - this.verifiedName, - this.verifiedPhone}); - - factory Owner.fromJson(Map json) { - return Owner( - address: json['address'] != null ? BillingAddress.fromJson(json['address']) : null, - email: json['email'], - name: json['name'], - phone: json['phone'], - verifiedAddress: json['verified_address'] != null ? BillingAddress.fromJson(json['verified_address']) : null, - verifiedEmail: json['verified_email'], - verifiedName: json['verified_name'], - verifiedPhone: json['verified_phone'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.address != null) data['address'] = this.address; - if (this.email != null) data['email'] = this.email; - if (this.name != null) data['name'] = this.name; - if (this.phone != null) data['phone'] = this.phone; - if (this.verifiedAddress != null) data['verified_address'] = this.verifiedAddress; - if (this.verifiedEmail != null) data['verified_email'] = this.verifiedEmail; - if (this.verifiedName != null) data['verified_name'] = this.verifiedName; - if (this.verifiedPhone != null) data['verified_phone'] = this.verifiedPhone; - return data; - } -} - -class AchCreditTransfer { - String accountNumber; - String bankName; - String fingerprint; - String routingNumber; - String swiftCode; - - AchCreditTransfer({this.accountNumber, this.bankName, this.fingerprint, this.routingNumber, this.swiftCode}); - - factory AchCreditTransfer.fromJson(Map json) { - return AchCreditTransfer( - accountNumber: json['account_number'], - bankName: json['bank_name'], - fingerprint: json['fingerprint'], - routingNumber: json['routing_number'], - swiftCode: json['swift_code'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.accountNumber != null) data['account_number'] = this.accountNumber; - if (this.bankName != null) data['bank_name'] = this.bankName; - if (this.fingerprint != null) data['fingerprint'] = this.fingerprint; - if (this.routingNumber != null) data['routing_number'] = this.routingNumber; - if (this.swiftCode != null) data['swift_code'] = this.swiftCode; - return data; - } -} diff --git a/lib/src/source_params.dart b/lib/src/source_params.dart deleted file mode 100644 index 1451db4b..00000000 --- a/lib/src/source_params.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stripe_payment/src/token.dart'; - -class SourceParams { - int amount; - String currency; - String returnURL; - String type; - String name; - String statementDescriptor; - String country; - String email; - CreditCard card; - - SourceParams( - {this.amount, - this.currency, - @required this.returnURL, - @required this.type, - this.name, - this.statementDescriptor, - this.country, - this.email, - this.card}); - - factory SourceParams.fromJson(Map json) { - return SourceParams( - amount: json['amount'], - currency: json['currency'], - returnURL: json['returnURL'], - type: json['type'], - name: json['name'], - statementDescriptor: json['statement_descriptor'], - country: json['country'], - email: json['email'], - card: json['card']); - } - - Map toJson() { - final Map data = new Map(); - if (this.amount != null) data['amount'] = this.amount; - if (this.currency != null) data['currency'] = this.currency; - if (this.returnURL != null) data['returnURL'] = this.returnURL; - if (this.type != null) data['type'] = this.type; - if (this.name != null) data['name'] = this.name; - if (this.statementDescriptor != null) data['statement_descriptor'] = this.statementDescriptor; - if (this.country != null) data['country'] = this.country; - if (this.email != null) data['email'] = this.email; - if (this.card != null) data['card'] = this.card.toJson(); - return data; - } -} diff --git a/lib/src/stripe_options.dart b/lib/src/stripe_options.dart new file mode 100644 index 00000000..7ffe6200 --- /dev/null +++ b/lib/src/stripe_options.dart @@ -0,0 +1,20 @@ +import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'stripe_options.g.dart'; + +@JsonSerializable() +class StripeOptions { + final String publishableKey; + final String merchantId; + final String androidPayMode; + + StripeOptions({ + @required this.publishableKey, + this.merchantId, + this.androidPayMode, + }); + + factory StripeOptions.fromJson(Map json) => _$StripeOptionsFromJson(json); + Map toJson() => _$StripeOptionsToJson(this); +} diff --git a/lib/src/stripe_options.g.dart b/lib/src/stripe_options.g.dart new file mode 100644 index 00000000..8bc25a33 --- /dev/null +++ b/lib/src/stripe_options.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stripe_options.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +StripeOptions _$StripeOptionsFromJson(Map json) { + return StripeOptions( + publishableKey: json['publishableKey'] as String, + merchantId: json['merchantId'] as String, + androidPayMode: json['androidPayMode'] as String, + ); +} + +Map _$StripeOptionsToJson(StripeOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('publishableKey', instance.publishableKey); + writeNotNull('merchantId', instance.merchantId); + writeNotNull('androidPayMode', instance.androidPayMode); + return val; +} diff --git a/lib/src/stripe_payment.dart b/lib/src/stripe_payment.dart index 6d1dec83..34543ae7 100644 --- a/lib/src/stripe_payment.dart +++ b/lib/src/stripe_payment.dart @@ -1,30 +1,42 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/foundation.dart'; +import 'package:stripe_payment/src/stripe_options.dart'; import 'android_pay_payment_request.dart'; import 'apple_pay_payment_request.dart'; -import 'card_form_payment_request.dart'; import 'error_codes.dart'; -import 'payment_intent.dart'; -import 'payment_method.dart'; -import 'source.dart'; -import 'source_params.dart'; -import 'token.dart'; +import 'model/bank_account.dart'; +import 'model/card_form_payment_request.dart'; +import 'model/credit_card.dart'; +import 'model/payment_intent.dart'; +import 'model/payment_intent_result.dart'; +import 'model/payment_method.dart'; +import 'model/payment_method_request.dart'; +import 'model/setup_intent_result.dart'; +import 'model/source.dart'; +import 'model/source_params.dart'; +import 'model/token.dart'; class StripePayment { static const MethodChannel _channel = const MethodChannel('stripe_payment'); /// https://tipsi.github.io/tipsi-stripe/docs/usage.html - static void setOptions(StripeOptions settings) { - _channel.invokeMethod('setOptions', {"options": settings.toJson(), "errorCodes": Errors.mapping}); + static Future setOptions(StripeOptions settings) async { + await _channel.invokeMethod('setOptions', { + "options": settings.toJson(), + "errorCodes": Errors.mapping, + }); } /// https://tipsi.github.io/tipsi-stripe/docs/usage.html - static void setStripeAccount(String stripeAccount) { - _channel.invokeMethod('setStripeAccount', {"stripeAccount": stripeAccount}); + static void setStripeAccount(String stripeAccount) async { + await _channel.invokeMethod('setStripeAccount', { + "stripeAccount": stripeAccount, + }); } /// https://tipsi.github.io/tipsi-stripe/docs/usage.html @@ -42,28 +54,78 @@ class StripePayment { } } - /// https://tipsi.github.io/tipsi-stripe/docs/canMakeNativePayPayments.html + /// https://tipsi.github.io/tipsi-stripe/docs/canmakeapplepaypaymentsoptions.html + /// https://tipsi.github.io/tipsi-stripe/docs/canmakeandroidpaypayments.html static Future canMakeNativePayPayments(List networks) async { if (kIsWeb) { throw UnimplementedError(); } else { if (Platform.isAndroid) { - return _channel.invokeMethod('canMakeAndroidPayPayments'); + return _channel.invokeMethod('canMakeAndroidPayPayments'); } else if (Platform.isIOS) { - Map options = {"networks": networks}; - return _channel.invokeMethod('canMakeApplePayPayments', options); + return _channel.invokeMethod('canMakeApplePayPayments', { + 'networks': networks, + }); } else throw UnimplementedError(); } } - static Future _deviceSupportsAndroidPay() => _channel.invokeMethod("deviceSupportsAndroidPay"); + static Future> potentiallyAvailableNativePayNetworks() async { + return await _channel.invokeListMethod( + "potentiallyAvailableNativePayNetworks", + ); + } + + static Future _deviceSupportsAndroidPay() { + return _channel.invokeMethod("deviceSupportsAndroidPay"); + } - static Future _deviceSupportsApplePay() => _channel.invokeMethod("deviceSupportsApplePay"); + static Future _deviceSupportsApplePay() { + return _channel.invokeMethod("deviceSupportsApplePay"); + } + + static Future paymentMethodFromNativePay({ + @required AndroidPayPaymentRequest androidPayOptions, + @required ApplePayPaymentOptions applePayOptions, + }) { + if (kIsWeb) { + throw UnimplementedError(); + } else { + if (Platform.isAndroid) { + return _paymentMethodFromAndroidPay(androidPayOptions); + } else if (Platform.isIOS) { + return _paymentMethodFromApplePay(applePayOptions); + } else + throw UnimplementedError(); + } + } + + static Future _paymentMethodFromAndroidPay( + AndroidPayPaymentRequest options) async { + final pm = await _channel.invokeMapMethod( + "paymentMethodFromAndroidPay", + options.toJson(), + ); + return PaymentMethod.fromJson(pm); + } + + static Future _paymentMethodFromApplePay( + ApplePayPaymentOptions options) async { + final pm = await _channel + .invokeMapMethod("paymentMethodFromApplePay", { + "options": options.toJson(), + "items": options.items.map((item) => item.toJson()).toList() + }); + print('received: $pm'); + return PaymentMethod.fromJson(pm); + } /// https://tipsi.github.io/tipsi-stripe/docs/paymentRequestWithNativePay.html - static Future paymentRequestWithNativePay( - {@required AndroidPayPaymentRequest androidPayOptions, @required ApplePayPaymentOptions applePayOptions}) { + static Future paymentRequestWithNativePay({ + @required AndroidPayPaymentRequest androidPayOptions, + @required ApplePayPaymentOptions applePayOptions, + }) { if (kIsWeb) { throw UnimplementedError(); } else { @@ -76,14 +138,22 @@ class StripePayment { } } - static Future _paymentRequestWithAndroidPay(AndroidPayPaymentRequest options) async { - final token = await _channel.invokeMethod("paymentRequestWithAndroidPay", options.toJson()); + static Future _paymentRequestWithAndroidPay( + AndroidPayPaymentRequest options) async { + final token = await _channel.invokeMapMethod( + "paymentRequestWithAndroidPay", + options.toJson(), + ); return Token.fromJson(token); } - static Future _paymentRequestWithApplePay(ApplePayPaymentOptions options) async { - final token = await _channel.invokeMethod("paymentRequestWithApplePay", - {"options": options.json, "items": options.items.map((item) => item.json).toList()}); + static Future _paymentRequestWithApplePay( + ApplePayPaymentOptions options) async { + final token = await _channel + .invokeMapMethod("paymentRequestWithApplePay", { + "options": options.toJson(), + "items": options.items.map((item) => item.toJson()).toList() + }); return Token.fromJson(token); } @@ -93,7 +163,7 @@ class StripePayment { throw UnimplementedError(); } else { if (Platform.isIOS) { - return _channel.invokeMethod("completeApplePayRequest"); + return await _channel.invokeMethod("completeApplePayRequest"); } else if (Platform.isAndroid) { return null; } else @@ -107,7 +177,7 @@ class StripePayment { throw UnimplementedError(); } else { if (Platform.isIOS) { - return _channel.invokeMethod("cancelApplePayRequest"); + return await _channel.invokeMethod("cancelApplePayRequest"); } else if (Platform.isAndroid) { return null; } else @@ -116,83 +186,94 @@ class StripePayment { } /// https://tipsi.github.io/tipsi-stripe/docs/paymentRequestWithCardForm.html - static Future paymentRequestWithCardForm(CardFormPaymentRequest options) async { - final token = await _channel.invokeMethod("paymentRequestWithCardForm", options.toJson()); + static Future paymentRequestWithCardForm( + CardFormPaymentRequest options) async { + final token = await _channel.invokeMapMethod( + "paymentRequestWithCardForm", + options.toJson(), + ); return PaymentMethod.fromJson(token); } /// https://tipsi.github.io/tipsi-stripe/docs/createTokenWithCard.html static Future createTokenWithCard(CreditCard card) async { - final token = await _channel.invokeMethod("createTokenWithCard", card.toJson()); + final token = await _channel.invokeMapMethod( + "createTokenWithCard", + card.toJson(), + ); return Token.fromJson(token); } /// https://tipsi.github.io/tipsi-stripe/docs/createTokenWithBankAccount.html static Future createTokenWithBankAccount(BankAccount options) async { - final token = await _channel.invokeMethod("createTokenWithBankAccount", options.toJson()); + final token = await _channel.invokeMapMethod( + "createTokenWithBankAccount", + options.toJson(), + ); return Token.fromJson(token); } /// https://tipsi.github.io/tipsi-stripe/docs/createsourcewithparamsparams.html static Future createSourceWithParams(SourceParams options) async { - final source = await _channel.invokeMethod("createSourceWithParams", options.toJson()); + final source = await _channel.invokeMapMethod( + "createSourceWithParams", + options.toJson(), + ); return Source.fromJson(source); } /// https://tipsi.github.io/tipsi-stripe/docs/createPaymentMethod.html - static Future createPaymentMethod(PaymentMethodRequest request) async { - final paymentMethod = await _channel.invokeMethod("createPaymentMethod", request.toJson()); + static Future createPaymentMethod( + PaymentMethodRequest request) async { + final paymentMethod = await _channel.invokeMapMethod( + "createPaymentMethod", + request.toJson(), + ); return PaymentMethod.fromJson(paymentMethod); } /// https://tipsi.github.io/tipsi-stripe/docs/authenticatePaymentIntent.html - static Future authenticatePaymentIntent({@required String clientSecret}) async { + static Future authenticatePaymentIntent( + {@required String clientSecret}) async { assert(clientSecret != null); - final result = await _channel.invokeMethod('authenticatePaymentIntent', {"clientSecret": clientSecret}); + final result = await _channel.invokeMapMethod( + 'authenticatePaymentIntent', + {"clientSecret": clientSecret}, + ); return PaymentIntentResult.fromJson(result); } /// https://tipsi.github.io/tipsi-stripe/docs/confirmPaymentIntent.html - static Future confirmPaymentIntent(PaymentIntent intent) async { + static Future confirmPaymentIntent( + PaymentIntent intent) async { assert(intent.clientSecret != null); - assert(intent.paymentMethodId != null); - final result = await _channel.invokeMethod('confirmPaymentIntent', intent.toJson()); + // assert(intent.paymentMethodId != null); + final result = await _channel.invokeMapMethod( + 'confirmPaymentIntent', + intent.toJson(), + ); return PaymentIntentResult.fromJson(result); } /// https://tipsi.github.io/tipsi-stripe/docs/authenticateSetupIntent.html - static Future authenticateSetupIntent({@required String clientSecret}) async { + static Future authenticateSetupIntent( + {@required String clientSecret}) async { assert(clientSecret != null); - final result = await _channel.invokeMethod('authenticateSetupIntent', {"clientSecret": clientSecret}); + final result = await _channel.invokeMapMethod( + 'authenticateSetupIntent', + {"clientSecret": clientSecret}, + ); return SetupIntentResult.fromJson(result); } /// https://tipsi.github.io/tipsi-stripe/docs/confirmSetupIntent.html - static Future confirmSetupIntent(PaymentIntent intent) async { + static Future confirmSetupIntent( + PaymentIntent intent) async { assert(intent.clientSecret != null); - assert(intent.paymentMethodId != null); - final result = await _channel.invokeMethod('confirmSetupIntent', intent.toJson()); + final result = await _channel.invokeMapMethod( + 'confirmSetupIntent', + intent.toJson(), + ); return SetupIntentResult.fromJson(result); } } - -class StripeOptions { - final String publishableKey; - final String merchantId; - final String androidPayMode; - - StripeOptions({@required this.publishableKey, this.merchantId, this.androidPayMode}); - - factory StripeOptions.fromJson(Map json) { - return StripeOptions( - merchantId: json['merchantId'], publishableKey: json['publishableKey'], androidPayMode: json['androidPayMode']); - } - - Map toJson() { - final Map data = new Map(); - if (this.merchantId != null) data['merchantId'] = this.merchantId; - if (this.publishableKey != null) data['publishableKey'] = this.publishableKey; - if (this.androidPayMode != null) data['androidPayMode'] = this.androidPayMode; - return data; - } -} diff --git a/lib/src/token.dart b/lib/src/token.dart deleted file mode 100644 index 37e084a7..00000000 --- a/lib/src/token.dart +++ /dev/null @@ -1,170 +0,0 @@ -class Token { - BankAccount bankAccount; - CreditCard card; - double created; - bool livemode; - String tokenId; - - Token({this.bankAccount, this.card, this.created, this.livemode, this.tokenId}); - - factory Token.fromJson(Map json) { - return Token( - bankAccount: json['bankAccount'] != null ? BankAccount.fromJson(json['bankAccount']) : null, - card: json['card'] != null ? CreditCard.fromJson(json['card']) : null, - created: json['created'] is int ? (json['created'] as int).toDouble() : json['created'], - livemode: json['livemode'], - tokenId: json['tokenId'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.created != null) data['created'] = this.created; - if (this.livemode != null) data['livemode'] = this.livemode; - if (this.tokenId != null) data['tokenId'] = this.tokenId; - if (this.bankAccount != null) { - data['bankAccount'] = this.bankAccount.toJson(); - } - if (this.card != null) { - data['card'] = this.card.toJson(); - } - return data; - } -} - -class BankAccount { - String accountHolderName; - String accountHolderType; - String accountNumber; - String bankName; - String countryCode; - String currency; - String fingerprint; - String last4; - String routingNumber; - - BankAccount( - {this.accountHolderName, - this.accountHolderType, - this.accountNumber, - this.bankName, - this.countryCode, - this.currency, - this.fingerprint, - this.last4, - this.routingNumber}); - - factory BankAccount.fromJson(Map json) { - return BankAccount( - accountHolderName: json['accountHolderName'], - accountHolderType: json['accountHolderType'], - accountNumber: json['accountNumber'], - bankName: json['bankName'], - countryCode: json['countryCode'], - currency: json['currency'], - fingerprint: json['fingerprint'], - last4: json['last4'], - routingNumber: json['routingNumber'], - ); - } - - Map toJson() { - final Map data = new Map(); - if (this.accountHolderName != null) data['accountHolderName'] = this.accountHolderName; - if (this.accountHolderType != null) data['accountHolderType'] = this.accountHolderType; - if (this.accountNumber != null) data['accountNumber'] = this.accountNumber; - if (this.bankName != null) data['bankName'] = this.bankName; - if (this.countryCode != null) data['countryCode'] = this.countryCode; - if (this.currency != null) data['currency'] = this.currency; - if (this.fingerprint != null) data['fingerprint'] = this.fingerprint; - data['last4'] = this.last4; - if (this.routingNumber != null) data['routingNumber'] = this.routingNumber; - return data; - } -} - -class CreditCard { - String addressCity; - String addressCountry; - String addressLine1; - String addressLine2; - String addressState; - String addressZip; - String brand; - String cardId; - String country; - String currency; - int expMonth; - int expYear; - String funding; - String last4; - String name; - String number; - String cvc; - String token; - - CreditCard( - {this.addressCity, - this.addressCountry, - this.addressLine1, - this.addressLine2, - this.addressState, - this.addressZip, - this.brand, - this.cardId, - this.currency, - this.country, - this.expMonth, - this.expYear, - this.number, - this.token, - this.cvc, - this.funding, - this.last4, - this.name}); - - factory CreditCard.fromJson(Map json) { - return CreditCard( - addressCity: json['addressCity'], - addressCountry: json['addressCountry'], - addressLine1: json['addressLine1'], - addressLine2: json['addressLine2'], - addressState: json['addressState'], - addressZip: json['addressZip'], - brand: json['brand'], - cardId: json['cardId'], - currency: json['currency'], - country: json['country'], - expMonth: json['expMonth'], - expYear: json['expYear'], - funding: json['funding'], - last4: json['last4'], - name: json['name'], - cvc: json['cvc'], - number: json['number'], - token: json['token']); - } - - Map toJson() { - final Map data = new Map(); - if (this.addressCity != null) data['addressCity'] = this.addressCity; - if (this.addressCountry != null) data['addressCountry'] = this.addressCountry; - data['addressLine1'] = this.addressLine1; - data['addressLine2'] = this.addressLine2; - if (this.addressState != null) data['addressState'] = this.addressState; - if (this.addressZip != null) data['addressZip'] = this.addressZip; - if (this.brand != null) data['brand'] = this.brand; - if (this.cardId != null) data['cardId'] = this.cardId; - if (this.country != null) data['country'] = this.country; - if (this.expMonth != null) data['expMonth'] = this.expMonth; - if (this.expYear != null) data['expYear'] = this.expYear; - if (this.funding != null) data['funding'] = this.funding; - if (this.currency != null) data['currency'] = this.currency; - data['last4'] = this.last4; - if (this.name != null) data['name'] = this.name; - if (this.number != null) data['number'] = this.number; - if (this.cvc != null) data['cvc'] = this.cvc; - if (this.token != null) data['token'] = this.token; - return data; - } -} diff --git a/lib/stripe_payment.dart b/lib/stripe_payment.dart index 9eca62c3..7725d8e6 100644 --- a/lib/stripe_payment.dart +++ b/lib/stripe_payment.dart @@ -1,10 +1,7 @@ +export 'src/android/line_item.dart'; export 'src/android_pay_payment_request.dart'; export 'src/apple_pay_payment_request.dart'; -export 'src/card_form_payment_request.dart'; +export 'src/model/all.dart'; export 'src/error_codes.dart'; -export 'src/payment_intent.dart'; -export 'src/payment_method.dart'; -export 'src/source.dart'; -export 'src/source_params.dart'; +export 'src/stripe_options.dart'; export 'src/stripe_payment.dart'; -export 'src/token.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 91bf8bd7..bfdd4a7b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,11 +3,9 @@ description: A Flutter plugin to integrate the stripe libraries for iOS and Andr version: 1.0.9 homepage: https://github.com/jonasbark/flutter_stripe_payment -environment: - sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.12.0 <2.0.0" - dependencies: + build_runner: ^1.0.0 + json_annotation: ^3.0.1 flutter: sdk: flutter @@ -15,7 +13,19 @@ flutter: plugin: platforms: android: - package: de.jonasbark.stripepayment + package: de.jonasbark.stripe_payment pluginClass: StripePaymentPlugin ios: pluginClass: StripePaymentPlugin + +dev_dependencies: + json_serializable: ^3.3.0 + flutter_test: + sdk: flutter + integration_test: ^1.0.1 + flutter_driver: + sdk: flutter + +environment: + sdk: ">=2.6.0 <3.0.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/test/stripe_payment_test.dart b/test/stripe_payment_test.dart new file mode 100644 index 00000000..bc6e19c2 --- /dev/null +++ b/test/stripe_payment_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stripe_payment/stripe_payment.dart'; + +void main() { + const MethodChannel channel = MethodChannel('stripe_payment'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); +}