Razorpay payment gateway in flutter mobile app - flutter

I am trying to integrate Razorpay into my mobile app. I understand the implementation and am able to generate/complete test payments from my app. Now when I read here about the authorization of payment I am a bit confused about it, what I should or need to save in server (apart from paymentId,orderId and signature)for future reference. I can see a tab transaction in the Razorpay dashboard which is showing me the status of payment(authorized/failed) then what is the use of this part. How can I use webhook for authorization?
Anyone who has implemented it please help me with this.

install flutter pub add razorpay_flutter pubdev
import 'package:razorpay_flutter/razorpay_flutter.dart';
create a instance of Razorpay by _razorpay = Razorpay();
it uses event-based communication and emits events like
EVENT_PAYMENT_SUCCESS
EVENT_PAYMENT_ERROR
EVENT_EXTERNAL_WALLET
so we have to listen to these events with respective functions to handle the payments.
#override
void initState() {
super.initState();
_razorpay = Razorpay();
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
}
void _handlePaymentSuccess(PaymentSuccessResponse response) {
var orderId = response.orderId;
var signature = response.signature;
var paymentId = response.paymentId;
//Do your stuff
}
void _handlePaymentError(PaymentFailureResponse response) {
var code= response.code;
var message = response.message ;
}
void _handleExternalWallet(ExternalWalletResponse response) {
}
void openCheckout() {
//create a Map which details
var options = {
"key": "Use Your API Key Id here",
"amount":amount,
"name": "test",
"description": "Test Payment",
"order_id": orderId.toString(),
"timeout": "60",
"theme.color": "#5eba7d",
"currency": "INR",
"prefill": {"contact": "546338833332", "email": "test#testing.com"},
};
try {
_razorpay.open(options);
} catch (e) {
print(e.toString());
}
}
in any click call openCheckout(); this function
at last, dispose of it
#override
void dispose() {
super.dispose();
_razorpay.clear();
}

Related

Flutter [in_app_purchase] Get all plans inside subscription

I'm using the in_app_purchase package, but I only can get one plan inside the subscriptions
I have 3 subscriptions:
Basic subscription
Premium subscription
Enterprise subscription
And inside each subscription, I want to have 2 plans:
Month plan
Year plan
I always get the plan that has the "backward compatibility"("This will be the baseline returned by the deprecated Google Play Billing Library method querySkuDetailsAsync()") enabled.
Is any way to get all plans, or do I have to have 6 subscriptions with only 1 plan in each one?
Edit:
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
import 'package:motorline_home/widgets/materials/appbar/appbar_title_widget.dart';
import 'package:motorline_home/widgets/materials/pop_button_widget.dart';
import 'package:rxdart/subjects.dart';
class SubscriptionPage extends StatefulWidget {
const SubscriptionPage({
Key? key,
}) : super(key: key);
#override
State<SubscriptionPage> createState() => _SubscriptionPageState();
}
class _SubscriptionPageState extends State<SubscriptionPage> {
// In app subscriptions
InAppPurchase _inAppPurchase = InAppPurchase.instance;
late StreamSubscription<List<PurchaseDetails>> _inAppPurchaseSubscription;
StreamController<List<ProductDetails>> _streamGooglePlaySubscriptions =
BehaviorSubject();
final List<String> _subscriptionsIDs = [
"basic",
"premium",
"enterprise",
];
#override
void initState() {
super.initState();
// In app purchase subscription
_inAppPurchaseSubscription =
_inAppPurchase.purchaseStream.listen((purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList);
}, onDone: () {
print("In app purchase onDone");
_inAppPurchaseSubscription.cancel();
}, onError: (error) {
print("In app purchase error: ${error.toString()}");
// handle error here.
_inAppPurchaseSubscription.cancel();
});
// Initialize in app purchase
_initializeInAppPurchase();
}
#override
void dispose() {
if (Platform.isIOS) {
final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
_inAppPurchase
.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
iosPlatformAddition.setDelegate(null);
}
// Cancel in app purchase listener
_inAppPurchaseSubscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: AppBarTitleWidget(
title: FlutterI18n.translate(context, "subscriptions"),
),
leading: PopButtonWidget(),
),
// Body
body: Container(),
);
}
void _initializeInAppPurchase() async {
print("Initializing in app purchase");
bool available = await _inAppPurchase.isAvailable();
print("In app purchase initialized: $available");
if (available) {
if (Platform.isIOS) {
final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
_inAppPurchase
.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
}
// Get subscriptions
List<ProductDetails> subscriptions = await _getSubscriptions(
productIds:
_subscriptionsIDs.toSet(),
);
// Sort by price
subscriptions.sort((a, b) => a.rawPrice.compareTo(b.rawPrice));
// Add subscriptions to stream
_streamGooglePlaySubscriptions.add(subscriptions);
// DEBUG: Print subscriptions
print("In app purchase subscription subscriptions: ${subscriptions}");
for (var subscription in subscriptions) {
print("In app purchase plan: ${subscription.id}: ${subscription.rawPrice}");
print("In app purchase description: ${subscription.description}");
// HOW GET ALL PLANS IN EACH SUBSCRIPTION ID?
}
await InAppPurchase.instance.restorePurchases();
}
}
// In app purchase updates
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
// If purchase is pending
if (purchaseDetails.status == PurchaseStatus.pending) {
print("In app purchase pending...");
// Show pending ui
} else {
if (purchaseDetails.status == PurchaseStatus.canceled) {
print("In app purchase cancelled");
}
// If purchase failed
if (purchaseDetails.status == PurchaseStatus.error) {
print("In app purchase error");
// Show error
} else if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
print("In app purchase restored or purchased");
}
if (purchaseDetails.pendingCompletePurchase) {
debugPrint("In app purchase complete purchased");
debugPrint(
"In app purchase purchase id : ${purchaseDetails.purchaseID}");
debugPrint(
"In app purchase server data : ${purchaseDetails.verificationData.serverVerificationData}");
debugPrint(
"In app purchase local data : ${purchaseDetails.verificationData.localVerificationData}");
// Verify purchase on backend
try {
// VALIDADE PURCHASE IN BACKEND
} catch (error) {
debugPrint("In app purchase error: ${error.toString()}");
}
}
}
});
}
// Get subscription
Future<List<ProductDetails>> _getSubscriptions(
{required Set<String> productIds}) async {
ProductDetailsResponse response =
await _inAppPurchase.queryProductDetails(productIds);
return response.productDetails;
}
}
/// Example implementation of the
/// [`SKPaymentQueueDelegate`](https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate?language=objc).
///
/// The payment queue delegate can be implementated to provide information
/// needed to complete transactions.
class ExamplePaymentQueueDelegate implements SKPaymentQueueDelegateWrapper {
#override
bool shouldContinueTransaction(
SKPaymentTransactionWrapper transaction, SKStorefrontWrapper storefront) {
return true;
}
#override
bool shouldShowPriceConsent() {
return false;
}
}
What you like to do is not possible. You need to create for each subscription a new plan, you can't say a Premium Subscription does have a yearly and a monthly plan.
This is not possible at the moment since it's not supported by official plugin (and we don't know when it will be):
https://github.com/flutter/flutter/issues/110909
As they mentioned, solution is one plan per subscription with the extra logic necessary in your app (e.g. to detect if subscription with ID PLUS-year is an upgrade of PLUS-month)

Flutter uni_links duplicate the app every time a link is clicked

I am implementing a password recovery function based on the url sent to the email. Opening the app based on that url was successful. But instead of directly opening the required page in the app that is in the background, it duplicates the app. Although it still leads me to the password recovery page, now there will be 2 same apps running side by side
Procedure
Enter your email to send the password reset link
Click submit
Open the email containing the recovery link
Duplicate the app and open a recovery password page
Things what happen
Splash screen, first page open in the app, I am trying to do as instructed from uni_links package but still no success. Currently the function getInitialLink has the effect of opening the app based on the recovery link
class SplashController extends GetxController {
final SharedPreferencesHelper _helper = Get.find<SharedPreferencesHelper>();
late StreamSubscription sub;
#override
void onReady() async {
super.onReady();
await checkToken();
}
Future<void> checkToken() async {
await Future.delayed(Duration(seconds: 3));
var token = _helper.getToken();
if (token == null) {
Get.offNamed(Routes.LOGIN);
} else {
Get.offNamed(Routes.MAIN);
}
}
#override
void onInit() {
super.onInit();
initUniLinks();
}
Future<Null> initUniLinks() async {
// Platform messages may fail, so we use a try/catch PlatformException.
try {
String? initialLink = await getInitialLink();
if (initialLink != null) {
print("okay man");
Get.toNamed(Routes.RECOVERY);
}
sub = getLinksStream().listen((link) {
}, onError: (err) {
});
} on PlatformException {
// Handle exception by warning the user their action did not succeed
// return?
}
}
}
I found the solution, actually this answer is already on Stackoverflow, and it's really simple.
In the AndroidManifest.xml file of the app. Find "android:launchMode" and change its old value to singleTask. And here is the result
android:launchMode="singleTask"

Fill a form when clicked on a link - Flutter

In my flutter app I want that whenever I send a link to any user that link will open my app and fill the form given at the page.
For example, if this is my app:
And then I sent this link through email : https://example.com/fillMyForm?username=johndoe
Then this will open my app and fill the form, something like this:
Thanks in advance.
You can use firebase dynamic links for that. Refer this link https://medium.com/better-programming/deep-linking-in-flutter-with-firebase-dynamic-links-8a4b1981e1eb
Retrieve dynamic link in flutter like this and open form screen when link get in register listener.
class MainWidgetState extends State<MainWidget> {
#override
void initState() {
super.initState();
this.initDynamicLinks();
}
initDynamicLinks(BuildContext context) async {
await Future.delayed(Duration(seconds: 3));
var data = await FirebaseDynamicLinks.instance.getInitialLink();
var deepLink = data?.link;
final queryParams = deepLink.queryParameters;
if (queryParams.length > 0) {
var userName = queryParams['userId'];
openFormScreen(userName);
}
FirebaseDynamicLinks.instance.onLink(onSuccess: (dynamicLink)
async {
var deepLink = dynamicLink?.link;
final queryParams = deepLink.queryParameters;
if (queryParams.length > 0) {
var userName = queryParams['userId'];
openFormScreen(userName);
}
debugPrint('DynamicLinks onLink $deepLink');
}, onError: (e) async {
debugPrint('DynamicLinks onError $e');
});
}
openFormScreen(String userName){
Navigator.of(context).pushNamed("routeFormScreen", arguments: {"name": userName});
}
}

Flutter - webRTC Video Call signalling doesn't work

I am able to implement voice and video call using agora.io library which is available at https://www.agora.io/ && https://github.com/AgoraIO/Flutter-SDK
how ever the process for starting a call is both user has to join a particular channel name defined by the user manually or automatically. which is not the practical way.
Is there any way to create a separate signalling system (may be using, nodejs socket, firebase or one-signal notification? )
What's the simultaneous/parallel way to be used along side that?
or what's the complete alternative?
Agora.io doesn't provide any method other passing a channel name manually or a default string. But what you can do is use Firebase dynamic link to share the channel name via a dynamic link. This link will redirect you to the page where you're taking channel name as input and fill the channel name according to the parameters passed. So your code will look something like:
class AgoraImpementation extends State<AgoraImplementation> {
#override
void initState() {
super.initState();
this.initDynamicLinks();
}
initDynamicLinks(BuildContext context) async {
await Future.delayed(Duration(seconds: 3));
var data = await FirebaseDynamicLinks.instance.getInitialLink();
var deepLink = data?.link;
final queryParams = deepLink.queryParameters;
if (queryParams.length > 0) {
var channelName = queryParams['channel_name'];
openFormScreen(channelName);
}
FirebaseDynamicLinks.instance.onLink(onSuccess: (dynamicLink)
async {
var deepLink = dynamicLink?.link;
final queryParams = deepLink.queryParameters;
if (queryParams.length > 0) {
var userName = queryParams['channel_name'];
openFormScreen(channelName);
}
debugPrint('DynamicLinks onLink $deepLink');
}, onError: (e) async {
debugPrint('DynamicLinks onError $e');
});
}
openFormScreen(String userName){
Navigator.of(context).pushNamed("routeFormScreen", arguments: {"channelName": channelName});
}
}

Trouble with WebSockets in Flutter

I am having some trouble implementing WebSockets in my flutter application.
Here is code my code:
void connectToWebSocket() {
print("trying to connect to websocket");
final Future futureChannel = establishConnection();
futureChannel.then((future) {
print("Connection established, registering interest now...");
channel = future;
webSocketConnected = true;
channel.sink.add({
"action": "saveConnection",
"UserName": "rakshak#gmail.com",
"DeviceId": "1d0032000947363339343638"
});
}).catchError((error) {
channel = null;
webSocketConnected = false;
webSocketConnectionError = error.toString();
print("Connection failed \n $webSocketConnectionError");
});
}
Future<IOWebSocketChannel> establishConnection() async {
final IOWebSocketChannel channel = IOWebSocketChannel.connect(
'wss://1j839fy6t3.execute-api.us-east-1.amazonaws.com/Dev');
return channel;
}
Nothing seems to happen when this code runs. I can see the print messages saying "trying to connect to WebSocket" and "Connection established, registering interest now..." on the console.
The WebSocket is implemented using AWS API Gateway and I can see in the logs that the Flutter app has not connected to the WebSocket.
I have tested the WebSocket using wscat command-line tool and I know that it works.
I am not seeing any error in the console.
Let me know if you would like to see any more of my code.
Turns out you channel.sink.add accepts a string and not a Map.
Replace
channel.sink.add({
"action": "saveConnection",
"UserName": "rakshak#gmail.com",
"DeviceId": "1d0032000947363339343638"
});
With
channel.sink.add('{
"action": "saveConnection",
"UserName": "rakshak#gmail.com",
"DeviceId": "1d0032000947363339343638"
}');
and it should work.
I don't understand what you want. But If you want websocket, you can refer below one.
Add Dart Pub
adhara_socket_io
Add class
class SignalServer {
static SignalServer _instatnce;
factory SignalServer() => _instatnce ?? new SignalServer._();
SignalServer._();
SocketIO socketIO;
int State = 0;
void ConnectServer() async {
this.socketIO = await SocketIOManager().createInstance("http://192.168.50.65:8081");
socketIO.onConnect((data) {
print("Signal server connected");
State = 1;
});
socketIO.onDisconnect((_) {
print("Signal Disconnected");
State = 0;
});
socketIO.connect();
}
}
For Instance(main.dart)
static SignalServer signalServer;
....
signalServer = new SignalServer();
signalServer.ConnectServer();
For use
In any widget
void initState() {
super.initState();
setSocketEvent();
}
void setSocketEvent() async{
await MyApp.signalServer.socketIO.on("room-ready", (data) {
//enter your code
});
await MyApp.signalServer.socketIO.on("on-message", (data) {
//enter your code
});
...
}
I hope it will help you.