Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync #1491

Merged
merged 9 commits into from
Nov 28, 2023
Merged

Sync #1491

Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added example/assets/revolut.png
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,90 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
import 'package:stripe_example/widgets/example_scaffold.dart';
import 'package:stripe_example/widgets/loading_button.dart';

import '../../config.dart';

class RevolutPayScreen extends StatelessWidget {
const RevolutPayScreen({Key? key}) : super(key: key);

Future<Map<String, dynamic>> _createPaymentIntent() async {
final url = Uri.parse('$kApiUrl/create-payment-intent');
final response = await http.post(
url,
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'currency': 'eur',
'payment_method_types': ['revolut_pay'],
'amount': 1099
}),
);

return json.decode(response.body);
}

Future<void> _pay(BuildContext context) async {
// Precondition:
//Make sure to have set a custom URI scheme in your app and add it to Stripe SDK
// see file main.dart in this example app.
// 1. on the backend create a payment intent for payment method and save the
// client secret.
final result = await _createPaymentIntent();
print('blaat $result');
final clientSecret = await result['clientSecret'];

// 2. use the client secret to confirm the payment and handle the result.
try {
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: clientSecret,
data: PaymentMethodParams.revolutPay(
paymentMethodData: PaymentMethodData(),
),
);

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Payment succesfully completed'),
),
);
} on Exception catch (e, s) {
throw e;
if (e is StripeException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Error from Stripe: ${e.error.localizedMessage ?? e.error.code}'),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Unforeseen error: ${e}'),
),
);
}
}
}

@override
Widget build(BuildContext context) {
return ExampleScaffold(
title: 'RevolutPay',
tags: ['Payment method'],
padding: EdgeInsets.all(16),
children: [
LoadingButton(
onPressed: () async {
await _pay(context);
},
text: 'Pay',
),
],
);
}
}
10 changes: 10 additions & 0 deletions example/lib/screens/screens.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:stripe_example/screens/regional_payment_methods/fpx_screen.dart'
import 'package:stripe_example/screens/regional_payment_methods/ideal_screen.dart';
import 'package:stripe_example/screens/regional_payment_methods/klarna_screen.dart';
import 'package:stripe_example/screens/regional_payment_methods/paypal_screen.dart';
import 'package:stripe_example/screens/regional_payment_methods/revolutpay_screen.dart';
import 'package:stripe_example/screens/regional_payment_methods/sofort_screen.dart';
import 'package:stripe_example/screens/regional_payment_methods/us_bank_account.dart';
import 'package:stripe_example/screens/wallets/apple_pay_screen.dart';
Expand Down Expand Up @@ -296,6 +297,15 @@ class Example extends StatelessWidget {
builder: (contex) => PayPalScreen(),
platformsSupported: [DevicePlatform.android, DevicePlatform.ios],
),
Example(
title: 'RevolutPay',
leading: Image.asset(
'assets/revolut.png',
width: 48,
),
builder: (context) => RevolutPayScreen(),
platformsSupported: [DevicePlatform.android, DevicePlatform.ios],
),
Example(
title: 'Us bank accounts (ACH)',
builder: (contex) => UsBankAccountScreen(),
Expand Down
9 changes: 7 additions & 2 deletions packages/stripe/lib/src/widgets/google_pay_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class GooglePayButton extends StatefulWidget {
required this.onTap,
this.type = GooglePayButtonType.pay,
this.buttonType = PlatformButtonType.pay,
this.borderRadius,
this.appearance = PlatformButtonStyle.automatic,
Key? key,
}) : super(key: key);

Expand All @@ -22,6 +24,8 @@ class GooglePayButton extends StatefulWidget {
@Deprecated('Use [buttonType] instead')
final GooglePayButtonType type;

final double? borderRadius;
final PlatformButtonStyle appearance;
final PlatformButtonType buttonType;
final VoidCallback onTap;
}
Expand All @@ -33,9 +37,10 @@ class _GooglePayButtonState extends State<GooglePayButton> {
@override
void initState() {
// ignore: deprecated_member_use_from_same_package
_creationParams['buttonType'] = describeEnum(widget.type);
_creationParams['buttonType'] = widget.type.name;
_creationParams['type'] = widget.buttonType.id;

_creationParams['appearance'] = widget.appearance.id;
_creationParams['borderRadius'] = widget.borderRadius;
super.initState();
}

Expand Down
5 changes: 3 additions & 2 deletions packages/stripe/lib/src/widgets/platform_pay_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ class PlatformPayButton extends StatelessWidget {
/// Defines the displayed text on the button.
final PlatformButtonType type;

/// iOS only, defines the color and border radius of the button
/// Defines the coloring of the button
final PlatformButtonStyle appearance;

/// iOS only, sets the border radius of the corners.
/// Sets the border radius of the corners.
final double borderRadius;

/// ios only, execute a callback when shipping
Expand Down Expand Up @@ -72,6 +72,7 @@ class PlatformPayButton extends StatelessWidget {
return GooglePayButton(
onTap: onPressed,
buttonType: type,
borderRadius: borderRadius,
);
} else if (Platform.isIOS) {
return ApplePayButton(
Expand Down
12 changes: 8 additions & 4 deletions 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.31.+'
ext.stripe_version = '20.34.+'

repositories {
google()
Expand Down Expand Up @@ -48,15 +48,19 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.12.0'

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"
implementation "com.stripe:stripe-android:$stripe_version"
implementation "com.stripe:financial-connections:$stripe_version"
implementation("com.stripe:stripe-android:$stripe_version") {
exclude group: 'androidx.emoji2', module: 'emoji2'
}
implementation ("com.stripe:financial-connections:$stripe_version") {
exclude group: 'androidx.emoji2', module: 'emoji2'
}
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'

// play-services-wallet is already included in stripe-android
compileOnly "com.google.android.gms:play-services-wallet:19.1.0"
compileOnly "com.google.android.gms:play-services-wallet:19.2.0"

// Users need to declare this dependency on their own, otherwise all methods are a no-op
compileOnly 'com.stripe:stripe-android-issuing-push-provisioning:1.1.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ class StripeSdkGooglePayButtonPlatformView(
if (creationParams?.containsKey("type") == true) {
googlePayButtonManager.type(payButton, creationParams["type"] as Int)
}
if (creationParams?.containsKey("appearance") == true) {
googlePayButtonManager.appearance(payButton, creationParams["appearance"] as Int)
}
if (creationParams?.containsKey("borderRadius") == true) {
googlePayButtonManager.borderRadius(payButton, creationParams["borderRadius"] as Int)
}
payButton.initialize()
payButton.getChildAt(0).setOnClickListener {
channel.invokeMethod("onPressed", null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ class GooglePayButtonManager : SimpleViewManager<GooglePayButtonView?>() {
view.setType(buttonType)
}

@ReactProp(name = "appearance")
fun appearance(view: GooglePayButtonView, appearance: Int) {
view.setAppearance(appearance)
}

@ReactProp(name = "borderRadius")
fun borderRadius(view: GooglePayButtonView, borderRadius: Int) {
view.setBorderRadius(borderRadius)
}

override fun createViewInstance(reactContext: ThemedReactContext): GooglePayButtonView {
return GooglePayButtonView(reactContext)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,91 @@
package com.reactnativestripesdk

import android.view.LayoutInflater
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.ThemedReactContext
import com.flutter.stripe.R
import com.google.android.gms.wallet.button.ButtonConstants.ButtonTheme
import com.google.android.gms.wallet.button.ButtonConstants.ButtonType
import com.google.android.gms.wallet.button.ButtonOptions
import com.google.android.gms.wallet.button.PayButton
import com.stripe.android.GooglePayJsonFactory
import org.json.JSONArray

class GooglePayButtonView(private val context: ThemedReactContext) : FrameLayout(context) {
private var button: View? = null
private var type: Int? = null
private var appearance: Int? = null
private var borderRadius: Int = 4 // Matches the default on iOS's ApplePayButton
private var button: PayButton? = null

fun initialize() {
val resAsset: Int =
when (type) {
0 -> R.layout.plain_googlepay_button
1 -> R.layout.buy_with_googlepay_button
6 -> R.layout.book_with_googlepay_button
5 -> R.layout.checkout_with_googlepay_button
4 -> R.layout.donate_with_googlepay_button
11 -> R.layout.order_with_googlepay_button
1000 -> R.layout.pay_with_googlepay_button
7 -> R.layout.subscribe_with_googlepay_button
1001 -> R.layout.googlepay_mark_button
else -> R.layout.plain_googlepay_button
}
if (button != null) {
removeView(button)
}
button = configureGooglePayButton()
addView(button)
viewTreeObserver.addOnGlobalLayoutListener { requestLayout() }
}

button = LayoutInflater.from(context).inflate(
resAsset, null
private fun configureGooglePayButton(): PayButton {
val googlePayButton = PayButton(
context
)
googlePayButton.initialize(buildButtonOptions())
googlePayButton.setOnClickListener { _ ->
// Call the Javascript TouchableOpacity parent where the onClick handler is set
(this.parent as? View)?.performClick() ?: run {
Log.e("StripeReactNative", "Unable to find parent of GooglePayButtonView.")
}
};
return googlePayButton
}

addView(button)
viewTreeObserver.addOnGlobalLayoutListener { requestLayout() }
private fun buildButtonOptions(): ButtonOptions {
val allowedPaymentMethods = JSONArray().put(
GooglePayJsonFactory(context).createCardPaymentMethod(
billingAddressParameters = null,
allowCreditCards = null
)
).toString()

val options = ButtonOptions.newBuilder()
.setAllowedPaymentMethods(allowedPaymentMethods)

getButtonType()?.let {
options.setButtonType(it)
}

getButtonTheme()?.let {
options.setButtonTheme(it)
}

options.setCornerRadius(PixelUtil.toPixelFromDIP(this.borderRadius.toDouble()).toInt())

return options.build()
}

private fun getButtonType(): Int? {
return when (this.type) {
0,
1 -> ButtonType.BUY
6 -> ButtonType.BOOK
5 -> ButtonType.CHECKOUT
4 -> ButtonType.DONATE
11 -> ButtonType.ORDER
7 -> ButtonType.SUBSCRIBE
1000 -> ButtonType.PAY
1001 -> ButtonType.PLAIN
else -> null
}
}

private fun getButtonTheme(): Int? {
return when (this.appearance) {
0, 1 -> ButtonTheme.LIGHT
2 -> ButtonTheme.DARK
else -> null
}
}

override fun requestLayout() {
Expand All @@ -43,9 +98,18 @@ class GooglePayButtonView(private val context: ThemedReactContext) : FrameLayout
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
button?.layout(left, top, right, bottom)

}

fun setType(type: Int) {
this.type = type
}

fun setAppearance(appearance: Int) {
this.appearance = appearance
}

fun setBorderRadius(borderRadius: Int) {
this.borderRadius = borderRadius
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class GooglePayLauncherFragment : Fragment() {
this.mode = mode
this.callback = callback
this.currencyCode = googlePayParams.getString("currencyCode") ?: "USD"
this.amount = googlePayParams.getInt("amount")
this.amount = getIntOrNull(googlePayParams, "amount")
this.label = googlePayParams.getString("label")
this.configuration = GooglePayLauncher.Config(
environment = if (googlePayParams.getBoolean("testEnv")) GooglePayEnvironment.Test else GooglePayEnvironment.Production,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class PaymentLauncherFragment(
StripeIntent.NextActionType.WeChatPayRedirect,
StripeIntent.NextActionType.UpiAwaitNotification,
StripeIntent.NextActionType.CashAppRedirect,
StripeIntent.NextActionType.SwishRedirect,
null, -> false
}
}
Expand Down
Loading
Loading