Android Project of Flutter
-
Add the line below to ‘repositories’ section of your project-level build.gradle file.
maven {
url "https://artifactory.paytm.in/libs-release-local"
}
-
Add the line below to ‘dependencies’ section of your App build.gradle.
implementation "com.paytm.appinvokesdk:appinvokesdk:1.5.3"
-
Add AllInOneSDKPlugin class in your project (see Appendix at the end of this document).
-
In the pre-existing MainApplication class, make changes as shared in doc (see Appendix).
-
Update setResult method in AllInOneSDKPlugin class as per your response requirement in the flutter app.
-
Call startTransaction method from your Flutter App to invoke Paytm module.
For further queries visit - https://developer.paytm.com/docs/all-in-one-sdk/.
Note: Please change minSDKversion to ‘18’ in your project level build.gradle file.
Flutter Project
- Import services.dart package.
import 'package:flutter/services.dart';
- Using MethodChannel create a link to AllInOneSDKPlugin of Android project.
var CHANNEL = "samples.flutter.dev/allInOne"; //CHANNEL as set in MainActivity of android project.
static const platform = const MethodChannel('CHANNEL');
- Start the process by calling platform.invokeMethod with the appropriate arguments.
var arguments = <String, dynamic>{
"mid": mid,
"orderId": orderId,
"amount": amount,
"txnToken": txnToken,
"callbackUrl": callbackUrl, //send this if custom callback url is required
"isStaging": isStaging
};
try {
var result = await platform.invokeMethod("startTransaction", arguments);
print(result.toString());
} catch (err) {
print(err.message);
}
Attributes |
Description |
Mandatory |
orderid
String(50)
|
Unique reference ID for a transaction which is generated by a merchant. Special characters allowed in Order ID are: "@" "-" "_" ".". |
Yes |
mid
String(20)
|
Unique identifier which is a part of your account credentials and is provided to every merchant by Paytm.It is different on staging and production environment. |
Yes |
txnToken
String
|
Transaction token received in response to the Initiate Transaction API request (Note - pass same order id in sdk which was used for initiateTransaction). |
Yes |
amount
String
|
Amount in INR payable by the customer. It should not include any separator like (",") and must contain digits up to two decimal points. |
Yes |
callbackurl
String(255)
|
On completion of transaction, Paytm Payment Gateway will send the response on this URL. This can be a static response URL as mentioned below:
- Staging Environment: "https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=<order_id>"
- Production Environment: "https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=<order_id>"
|
Yes |
IsStaging
Boolean
|
IsStaging is to define staging or production server (True for staging and False for production) |
Yes |
- Callback will be received in the above “result” as below string in case of transaction success or failure else “error will be thrown” which will have a string message like onBackedPressed, networkError etc. You can handle this as per your requirement in the AllInOneSDKPlugin.setResult method which is in your flutter android project.
-
In the case of App Invoke flow, the response will be received in onActivityResult.
-
In the case of Redirection flow (WebView), the response will be received in PaytmPaymentTransactionCallback interface.
Sample Response
Bundle[
{
STATUS=TXN_SUCCESS,
ORDERID="Order Id",
CHARGEAMOUNT=0.00,
TXNAMOUNT=1.00,
TXNDATE=2020-07-21 19:00:05.0,
MID="Merchant Id",
TXNID="Transaction Value",
RESPCODE=01,
PAYMENTMODE=UPI,
BANKTXNID="Bank transaction Id",
CURRENCY=INR,
GATEWAYNAME=ICICI,
RESPMSG=Txn Success
}
]
-
Verifying Payment
-
You should validate the transaction response via a server-side request using the Transaction Status API. This API requires checksumhash in request and response. You must verify the Order ID and Amount with your data. The status should be treated as the final status of the transaction in all cases.
-
Paytm provides payment response on both Callback URL and Webhook URL. Please refer to the sample response for different payment sources here.
Appendix
MainActivity.kt
package
import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/allInOne" //Change as per project
private lateinit var plugin : AllInOneSDKPlugin
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,CHANNEL).setMethodCallHandler { call, result ->
plugin = AllInOneSDKPlugin(this,call,result)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
plugin.onActivityResult(requestCode,resultCode,data)
}
}
AllInOneSDKPlugin.kt
package
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import com.paytm.pgsdk.PaytmOrder
import com.paytm.pgsdk.PaytmPaymentTransactionCallback
import com.paytm.pgsdk.TransactionManager
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class AllInOneSDKPlugin(var activity: Activity, var call: MethodCall, var result: MethodChannel.Result) {
private val REQ_CODE = 0
init {
if (call.method == "startTransaction") {
startTransaction()
}
}
private fun startTransaction() {
val arg = call.arguments as Map<*, *>?
if (arg != null) {
val mid = arg["mid"] as String?
val orderId = arg["orderId"] as String?
val amount = arg["amount"] as String?
val txnToken = arg["txnToken"] as String?
val callbackUrl = arg["callbackUrl"] as String?
val isStaging = arg["isStaging"] as Boolean
if (mid == null || orderId == null || amount == null || mid.isEmpty() || orderId.isEmpty() || amount.isEmpty()) {
showToast("Please enter all field")
return
}
if (txnToken == null || txnToken.isEmpty()) {
showToast("Token error")
return
}
initiateTransaction(mid, orderId, amount, txnToken, callbackUrl, isStaging)
} else {
showToast("Please send arguments")
}
}
private fun initiateTransaction(mid: String, orderId: String, amount: String, txnToken: String, callbackUrl: String?, isStaging: Boolean) {
var host = "https://securegw.paytm.in/"
if (isStaging) {
host = "https://securegw-stage.paytm.in/"
}
val callback = if (callbackUrl == null || callbackUrl.trim().isEmpty()) {
host + "theia/paytmCallback?ORDER_ID=" + orderId
} else {
callbackUrl
}
val paytmOrder = PaytmOrder(orderId, mid, txnToken, amount, callback)
val transactionManager = TransactionManager(paytmOrder, object : PaytmPaymentTransactionCallback {
override fun onTransactionResponse(bundle: Bundle) {
//Return in both cases if transaction is success or failure
setResult("Payment Transaction response $bundle", true)
}
override fun networkNotAvailable() {
setResult("networkNotAvailable", false)
}
override fun onErrorProceed(s: String) {
setResult(s, false)
}
override fun clientAuthenticationFailed(s: String) {
setResult(s, false)
}
override fun someUIErrorOccurred(s: String) {
setResult(s, false)
}
override fun onErrorLoadingWebPage(iniErrorCode: Int, inErrorMessage: String, inFailingUrl: String) {
setResult(inErrorMessage, false)
}
override fun onBackPressedCancelTransaction() {
setResult("onBackPressedCancelTransaction", false)
}
override fun onTransactionCancel(s: String, bundle: Bundle) {
setResult("$s $bundle", false)
}
})
transactionManager.setShowPaymentUrl(host + "theia/api/v1/showPaymentPage")
transactionManager.startTransaction(activity, REQ_CODE)
}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQ_CODE && data != null) {
val message = data.getStringExtra("nativeSdkForMerchantMessage")
val response = data.getStringExtra("response")
// data.getStringExtra("nativeSdkForMerchantMessage") this return message if transaction was stopped by users
// data.getStringExtra("response") this returns the shared response if the transaction was successful or failure.
if(response !=null && response.isNotEmpty()){
setResult(response,true)
}else{
setResult(message,false)
}
}
}
private fun setResult(message: String, isSuccess: Boolean) {
if (isSuccess) {
result.success(message)
} else {
result.error("0", message, null)
}
}
private fun showToast(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
}