Skip to content

Commit

Permalink
Sync with Stripe React Native 0.27.0 (#1213)
Browse files Browse the repository at this point in the history
* Sync with Stripe React Native 0.27.0

* add 3ds usage data to card payment

* Feat: add  BillingDetailsCollectionConfiguration to paymentsheet. This can be used to collect email,phone,name or address in Paymentsheet

* Feat: provide choice to render the cardfield on specific android platformview

---------

Co-authored-by: Remon <>
  • Loading branch information
jonasbark committed Apr 30, 2023
1 parent 4b1b7ee commit 909d069
Show file tree
Hide file tree
Showing 26 changed files with 859 additions and 47 deletions.
65 changes: 53 additions & 12 deletions packages/stripe/lib/src/widgets/card_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class CardField extends StatefulWidget {
this.cvcHintText,
this.postalCodeHintText,
this.controller,
this.androidPlatformViewRenderType =
AndroidPlatformViewRenderType.expensiveAndroidView,
}) : super(key: key);

/// Decoration related to the input fields.
Expand Down Expand Up @@ -96,6 +98,12 @@ class CardField extends StatefulWidget {
/// Default is `false`.
final bool dangerouslyUpdateFullCardDetails;

/// Type of platformview used for rendering on Android.
///
/// This is an advanced option and changing this should be tested on multiple android devices.
/// Defaults to [AndroidPlatformViewRenderType.expensiveAndroidView]
final AndroidPlatformViewRenderType androidPlatformViewRenderType;

@override
// ignore: library_private_types_in_public_api
_CardFieldState createState() => _CardFieldState();
Expand Down Expand Up @@ -171,6 +179,8 @@ class _CardFieldState extends State<CardField> {
child: _MethodChannelCardField(
controller: controller,
height: platformCardHeight,
androidPlatformViewRenderType:
widget.androidPlatformViewRenderType,
focusNode: _node,
style: style,
placeholder: placeholder,
Expand Down Expand Up @@ -258,6 +268,7 @@ class _MethodChannelCardField extends StatefulWidget {
_MethodChannelCardField({
this.onCardChanged,
required this.controller,
required this.androidPlatformViewRenderType,
Key? key,
this.onFocus,
this.style,
Expand Down Expand Up @@ -290,6 +301,7 @@ class _MethodChannelCardField extends StatefulWidget {
final CardEditController controller;
final bool dangerouslyGetFullCardDetails;
final bool dangerouslyUpdateFullCardDetails;
final AndroidPlatformViewRenderType androidPlatformViewRenderType;

// This is used in the platform side to register the view.
static const _viewType = 'flutter.stripe/card_field';
Expand Down Expand Up @@ -392,6 +404,7 @@ class _MethodChannelCardFieldState extends State<_MethodChannelCardField>
viewType: _MethodChannelCardField._viewType,
creationParams: creationParams,
onPlatformViewCreated: onPlatformViewCreated,
androidPlatformViewRenderType: widget.androidPlatformViewRenderType,
);
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
platform = Listener(
Expand Down Expand Up @@ -583,8 +596,10 @@ class _AndroidCardField extends StatelessWidget {
required this.viewType,
required this.creationParams,
required this.onPlatformViewCreated,
required this.androidPlatformViewRenderType,
}) : super(key: key);

final AndroidPlatformViewRenderType androidPlatformViewRenderType;
final String viewType;
final Map<String, dynamic> creationParams;
final PlatformViewCreatedCallback onPlatformViewCreated;
Expand All @@ -602,18 +617,34 @@ class _AndroidCardField extends StatelessWidget {
),
onCreatePlatformView: (params) {
onPlatformViewCreated(params.id);
return PlatformViewsService.initExpensiveAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: Directionality.of(context),
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
switch (androidPlatformViewRenderType) {
case AndroidPlatformViewRenderType.expensiveAndroidView:
return PlatformViewsService.initExpensiveAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: Directionality.of(context),
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
case AndroidPlatformViewRenderType.androidView:
return PlatformViewsService.initAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: Directionality.of(context),
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
}
},
);
}
Expand Down Expand Up @@ -646,3 +677,13 @@ const kCardFieldDefaultHeight = 48.0;
const kCardFieldDefaultFontSize = 17;
const kCardFieldDefaultTextColor = Colors.black;
const kCardFieldDefaultFontFamily = 'Roboto';

enum AndroidPlatformViewRenderType {
/// Controls an Android view that is composed using the Android view hierarchy
expensiveAndroidView,

/// Use an Android view composed using a GL texture.
///
/// This is more efficient but has more issues on older Android devices.
androidView,
}
2 changes: 1 addition & 1 deletion packages/stripe_android/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.8.0'
ext.stripe_version = '[20.20.0, 20.22.0['
ext.stripe_version = '20.23.+'

repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class PaymentSheetFragment(
val googlePayConfig = buildGooglePayConfig(arguments?.getBundle("googlePay"))
val allowsDelayedPaymentMethods = arguments?.getBoolean("allowsDelayedPaymentMethods")
val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails")
val billingConfigParams = arguments?.getBundle("billingDetailsCollectionConfiguration")
paymentIntentClientSecret = arguments?.getString("paymentIntentClientSecret").orEmpty()
setupIntentClientSecret = arguments?.getString("setupIntentClientSecret").orEmpty()
val appearance = try {
Expand Down Expand Up @@ -119,6 +120,15 @@ class PaymentSheetFragment(
}
}

val billingDetailsConfig = PaymentSheet.BillingDetailsCollectionConfiguration(
name = mapToCollectionMode(billingConfigParams?.getString("name")),
phone = mapToCollectionMode(billingConfigParams?.getString("phone")),
email = mapToCollectionMode(billingConfigParams?.getString("email")),
address = mapToAddressCollectionMode(billingConfigParams?.getString("address")),
attachDefaultsToPaymentMethod = billingConfigParams?.getBoolean("attachDefaultsToPaymentMethod")
?: false
)

var defaultBillingDetails: PaymentSheet.BillingDetails? = null
if (billingDetailsBundle != null) {
val addressBundle = billingDetailsBundle.getBundle("address")
Expand Down Expand Up @@ -147,7 +157,8 @@ class PaymentSheetFragment(
googlePay = googlePayConfig,
appearance = appearance,
shippingDetails = shippingDetails,
primaryButtonLabel = primaryButtonLabel
primaryButtonLabel = primaryButtonLabel,
billingDetailsCollectionConfiguration = billingDetailsConfig
)

if (arguments?.getBoolean("customFlow") == true) {
Expand Down Expand Up @@ -300,3 +311,21 @@ fun getBase64FromBitmap(bitmap: Bitmap?): String? {
val imageBytes: ByteArray = stream.toByteArray()
return Base64.encodeToString(imageBytes, Base64.DEFAULT)
}

fun mapToCollectionMode(str: String?): PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode {
return when (str) {
"automatic" -> PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Automatic
"never" -> PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Never
"always" -> PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always
else -> PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Automatic
}
}

fun mapToAddressCollectionMode(str: String?): PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode {
return when (str) {
"automatic" -> PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Automatic
"never" -> PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Never
"full" -> PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Full
else -> PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Automatic
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ internal fun mapFromPaymentMethod(paymentMethod: PaymentMethod): WritableMap {
it.putString("fingerprint", paymentMethod.card?.fingerprint)
it.putString("preferredNetwork", paymentMethod.card?.networks?.preferred)
it.putArray("availableNetworks", paymentMethod.card?.networks?.available?.toList() as? ReadableArray)
it.putMap("threeDSecureUsage", WritableNativeMap().also { threeDSecureUsageMap ->
threeDSecureUsageMap.putBoolean("isSupported", paymentMethod.card?.threeDSecureUsage?.isSupported ?: false)
})
})
pm.putMap("SepaDebit", WritableNativeMap().also {
it.putString("bankCode", paymentMethod.sepaDebit?.bankCode)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#e0ffffff">
<item
android:drawable="@drawable/googlepay_button_background_image" />
</ripple>

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#e0ffffff">
<item
android:drawable="@drawable/googlepay_button_no_shadow_background_image" />
</ripple>

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<size android:width="270dp" android:height="49dp"/>
<solid android:color="#000"/>
<stroke android:color="#747775" android:width="1dp"/>
<corners android:radius="24dp"/>
</shape>
</item>
</layer-list>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@drawable/googlepay_button_background_image" />
</selector>
9 changes: 6 additions & 3 deletions packages/stripe_ios/ios/Classes/Stripe Sdk/Mappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class Mappers {
"isPending": shippingMethod.type == .pending,
"label": shippingMethod.label
]

if #available(iOS 15.0, *) {
if let dateComponentsRange = shippingMethod.dateComponentsRange {
method.setObject(
Expand Down Expand Up @@ -612,8 +612,11 @@ class Mappers {
"last4": paymentMethod.card?.last4 ?? NSNull(),
"preferredNetwork": paymentMethod.card?.networks?.preferred ?? NSNull(),
"availableNetworks": paymentMethod.card?.networks?.available ?? NSNull(),
"threeDSecureUsage": [
"isSupported": paymentMethod.card?.threeDSecureUsage?.supported ?? false
],
]

let sepaDebit: NSDictionary = [
"bankCode": paymentMethod.sepaDebit?.bankCode ?? NSNull(),
"country": paymentMethod.sepaDebit?.country ?? NSNull(),
Expand Down Expand Up @@ -950,7 +953,7 @@ class Mappers {
}
return nil
}

class func convertDateToUnixTimestampSeconds(date: Date?) -> String? {
if let date = date {
let value = date.timeIntervalSince1970
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ extension StripeSdk {
if let allowsDelayedPaymentMethods = params["allowsDelayedPaymentMethods"] as? Bool {
configuration.allowsDelayedPaymentMethods = allowsDelayedPaymentMethods
}

if let billingConfigParams = params["billingDetailsCollectionConfiguration"] as? [String: Any?] {
configuration.billingDetailsCollectionConfiguration.name = StripeSdk.mapToCollectionMode(str: billingConfigParams["name"] as? String)
configuration.billingDetailsCollectionConfiguration.phone = StripeSdk.mapToCollectionMode(str: billingConfigParams["phone"] as? String)
configuration.billingDetailsCollectionConfiguration.email = StripeSdk.mapToCollectionMode(str: billingConfigParams["email"] as? String)
configuration.billingDetailsCollectionConfiguration.address = StripeSdk.mapToAddressCollectionMode(str: billingConfigParams["address"] as? String)
configuration.billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod = billingConfigParams["attachDefaultsToPaymentMethod"] as? Bool == true
}

if let defaultBillingDetails = params["defaultBillingDetails"] as? [String: Any?] {
configuration.defaultBillingDetails.name = defaultBillingDetails["name"] as? String
Expand Down Expand Up @@ -163,4 +171,31 @@ extension StripeSdk {
}
})
}

private static func mapToCollectionMode(str: String?) -> PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode {
switch str {
case "automatic":
return .automatic
case "never":
return .never
case "always":
return .always
default:
return .automatic
}
}

private static func mapToAddressCollectionMode(str: String?) -> PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode {
switch str {
case "automatic":
return .automatic
case "never":
return .never
case "full":
return .full
default:
return .automatic
}
}
}

2 changes: 1 addition & 1 deletion packages/stripe_ios/ios/stripe_ios.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint stripe_ios.podspec' to validate before publishing.
#
stripe_version = '~> 23.5.0'
stripe_version = '23.6.0'
Pod::Spec.new do |s|
s.name = 'stripe_ios'
s.version = '0.0.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ class Card with _$Card {

/// The available networks the card can run.
List<String>? availableNetworks,

/// Three 3ds usage data.
ThreeDSecureUsage? threeDSecureUsage,
}) = _Card;

factory Card.fromJson(Map<String, dynamic> json) => _$CardFromJson(json);
Expand Down Expand Up @@ -806,3 +809,18 @@ class MandateDataOnlineData with _$MandateDataOnlineData {
factory MandateDataOnlineData.fromJson(Map<String, dynamic> json) =>
_$MandateDataOnlineDataFromJson(json);
}


@freezed

Check warning on line 814 in packages/stripe_platform_interface/lib/src/models/payment_methods.dart

View workflow job for this annotation

GitHub Actions / Typo CI

freezed

"freezed" is a typo. Did you mean "freezes"?
class ThreeDSecureUsage with _$ThreeDSecureUsage {
/// Data associated with the 3ds usage.
@JsonSerializable(explicitToJson: true)
const factory ThreeDSecureUsage({
/// Whether 3ds is supported or not.
bool? isSupported,
}) = _ThreeDSecureUsage;

factory ThreeDSecureUsage.fromJson(Map<String, dynamic> json) =>
_$ThreeDSecureUsageFromJson(json);
}
Loading

0 comments on commit 909d069

Please sign in to comment.