PayPal Pub Hell in Flutter
A while ago I uploaded a video here on YouTube in which I talked to you about the hell of webviews:
But I'll quickly summarize it for you in case you also come in here in context with what I'm going to talk to you about, the nightmare was that for each operating system, for each platform and also for each operation, that is to say, if you wanted to either see HTML content through a URL, that is to say, you try the URL and it simply renders it as if it were a browser or you directly have the HTML code and you simply want to represent it, it was a nightmare because for Android iOS we had one, for Mac we had others that none of them worked for me, for Windows we had another and so on, therefore, sometimes to simply render some, as I tell you, whether it be an HTML page or directly HTML content, it was a nightmare for all platforms and I had like four and I still have several there because of what was mentioned, but it seems that those waters bring us another problem
The problem with PayPal plugins
That is to say, that is where we come from and that is why I am making the introduction to what the webview was, we have the nightmare or hell with the plugins or extensions for PayPal for Flutter in which we have a similar problem and as I told you, I mentioned the webview simply because these plugins basically work in a similar way, they simply install a blessed webview internally and from there simply make the payment.
flutter_paypal
This plugin, at the moment I say these words, causes the application to not start, because its dependency is very old:
A problem occurred configuring project ':webview_flutter_x5'.
> Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
> Namespace not specified. Specify a namespace in the module's build file: /Users/andrescruz/.pub-cache/hosted/pub.dev/webview_flutter_x5-2.0.8+9/android/build.gradle. See https://d.android.com/r/tools/upgrade-assistant/set-namespace for information about setting the namespace.
Note that if I remove it, well first I'm going to see the error, obviously here you can also run what is the Grande build of Android, it will not do it right now because I do not want the video to take half an hour, but I have already done it, what I mean is the error is due to the dependency of the webview:
webview_flutter_x5
By the way, none of these plugins are specific to PayPal, which also sometimes bothers me a bit and is also the problem we have here at Flutter, as they say we have mobile development and then at the top we have Flutter in the pyramid, meaning mobile development as the base of the pyramid would be Scout or directly Android Studio, as if companies do not care about placing many of them, I would create a native plugin for the platforms that would be greatly appreciated as they did in Stripe although this TR is a small nightmare:
To use the plugin:
payPackWithPaypalPlugin(
String price, String tagPack, String userToken, BuildContext context,
[String coupon = '']) {
return UsePaypal(
sandboxMode: !appPayPalProduction,
clientId: paypalClientId,
secretKey: paypalSecret,
returnURL: "https://example.com/success",
cancelURL: "https://example.com/cancel",
transactions: [
{
"amount": {
"total": price,
"currency": "USD",
},
"description":
"${LocaleKeys.thankYouForPurchasingThisCourse.tr()}: $tagPack",
"item_list": {
"items": [
{
"name": tagPack,
"quantity": 1,
"price": price,
"currency": "USD"
}
],
}
}
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) {
print("onSuccess: $params");
},
onError: (error) {
print("onError: $error");
},
onCancel: (params) {
print('cancelled: $params');
});
}
Configure the plugin because we have to configure at least like five things but that's another story but at least it's here
We also have this plugin that chtp recommended to me, which I couldn't find by the way and it's a little complicated. Here I'm going to show you a little bit of the code since, well, enjoy, I'm putting it here. This is the PayPal flurer. Note that the integration is simple, it's simply a function. Here we establish what the amount is and for the rest, here we have the on success or on error or on cancel and there you can do something and it's a screen like this. Note that it's a web page, there you have the URL here.
paypal_sdk
This is the PayPal SDK meanwhile by the way since I removed this one I'm going to run it to save time this is the other PayPal SDK that I've also been using but it's a little nightmare because the joke is that you create everything as if you had taken my Laravel course basically here you put together the whole order manually and at the end it gives you a blessed link so that you can launch it again we come back again with the web part not so that you launch it to the browser or wherever god knows you want and from there it processes the order:
// Crea la orden de PayPal y genera el enlace de pago
Future<Order?> createOrder(String amount) async {
// claves
var paypalEnvironment =
appPayPalProduction
? PayPalEnvironment.live(
clientId: paypalClientId,
clientSecret: paypalSecret,
)
: PayPalEnvironment.sandbox(
clientId: paypalClientId,
clientSecret: paypalSecret,
);
// client
var payPalHttpClient = PayPalHttpClient(
paypalEnvironment /*, accessToken: accessToken*/,
accessTokenUpdatedCallback: (accessToken) async {
// Persist token for re-use
},
);
// crea la ordenApi
final ordersApi = OrdersApi(payPalHttpClient);
try {
// orden
final orderRequest = OrderRequest(
intent: OrderRequestIntent.authorize,
purchaseUnits: [
PurchaseUnitRequest(
// amount: AmountWithBreakdown(currencyCode: "USD", value: "10.00"),
amount: AmountWithBreakdown(currencyCode: "USD", value: amount),
),
],
);
// creamos la orden
final order = await ordersApi.createOrder(orderRequest);
// link para procesar la orden
String? approveUrl =
order.links?.firstWhere((link) => link.rel == "approve").href;
// la orden fue creada exitosamente
if (approveUrl != null && approveUrl.isNotEmpty) {
await launchUrl(
Uri.parse(approveUrl),
mode: LaunchMode.externalApplication,
);
print("Respuesta de la orden: ${jsonEncode(order.toJson())}");
return order;
} else {
print("⚠ No se pudo obtener la URL de aprobación.");
}
} catch (e) {
print("Error al crear la orden: $e");
}
return null;
}
So it's a nightmare because obviously when you leave the mobile application and go to a web application, that is to say to another application directly or better said when you leave applications on an Android phone or on any device you no longer have a direct way of knowing when the operation is going to end, so you have to place a button here when I return to the application or something like that, that was what occurred to me so that they synchronize the operations, therefore I did not like the implementation even
flutter_paypal_payment
This other plugin, I had problems before on MacOS when making the payment, it gave me the error:
Your PayPal credentials seems incorrect flutter_paypal_payment
But only on MacOS, as time passes and when updating dependencies and Flutter, that error no longer appears, but in the emulator the order remains processing:
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => PaypalCheckoutView(
sandboxMode: true,
clientId: "",
secretKey: "",
transactions: const [
{
"amount": {
"total": '70',
"currency": "USD",
"details": {
"subtotal": '70',
"shipping": '0',
"shipping_discount": 0
}
},
"description": "The payment transaction description.",
// "payment_options": {
// "allowed_payment_method":
// "INSTANT_FUNDING_SOURCE"
// },
"item_list": {
"items": [
{
"name": "Apple",
"quantity": 4,
"price": '5',
"currency": "USD"
},
{
"name": "Pineapple",
"quantity": 5,
"price": '10',
"currency": "USD"
}
],
// shipping address is not required though
// "shipping_address": {
// "recipient_name": "tharwat",
// "line1": "Alexandria",
// "line2": "",
// "city": "Alexandria",
// "country_code": "EG",
// "postal_code": "21505",
// "phone": "+00000000",
// "state": "Alexandria"
// },
}
}
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
print("onSuccess: $params");
},
onError: (error) {
print("onError: $error");
Navigator.pop(context);
},
onCancel: () {
print('cancelled:');
},
),
));