Razorpay android SDK integration issue - android

When order_id is used in the JSON of checkout object error is occurred
The error that I am getting is this:
06-23 14:08:44.132 E/PaymentActivity:166: The id provided does not exist
This comes in:
onPaymentError(code: Int, response: String?, data: PaymentData?)
method of PaymentResultWithDataListener
startPayment method is:
private fun startPayment(orderId: Long, paymentGateway: PaymentGateway) {
val checkout = Checkout()
checkout.setImage(R.drawable.lifcare_logo)
checkout.setFullScreenDisable(true)
try {
val options = JSONObject()
options.put("name", "Name")
options.put("description", orderId.toString())
options.put("currency", "INR")
options.put("amount", paymentGateway.amount.times(100))
options.put("order_id", paymentGateway.refTransactionId)
val prefill = JSONObject()
prefill.put("email", "EmailID")
prefill.put("contact", "Number")
prefill.put("method", paymentGateway.subMethod?.name)
options.put("prefill", prefill)
options.put("theme", JSONObject("{color: '#7cb342'}"))
checkout.open(this, options)
} catch (e: Exception) {
Timber.e(e, "Cannot pay right now!!")
}
}
ref_transaction_id is: "ref_transaction_id": "order_AQjijq5Fj4lg8m"
When order_id is not used then the SDK is working fine. order_id somehow is creating the issue.

May i know what number are you using?
It does not work for default number like '9999999999'.
The number should be a valid number.

Try to add notes key in option parameter as given below:
jsonObject.put("order_id", "12345")
jsonObject.put("subscription_id", "50214")
jsonObject.put("user_id", "101")
options.put("notes", jsonObject)

Related

How to handle JSON with multiple values and Moshi

When communicating with some APIs, it can happen that the JSON body may have duplicate fields which can cause the following error:
com.squareup.moshi.JsonDataException: Multiple values for 'name' at $.person.name
This can happen when the API delivers something similar to as follows:
{
"name" : "Chuck",
"age" : 21,
"name" : "Chuck"
}
How to handle such an issue?
I tried researching the web to see what I can find and found a similar answer here which handles duplicate data as a list and adding it together, but not showing a simple way as to how to ignore or overwrite it.
There is also confirmation that Moshi does not support this yet or may not even support it in the future as it is more of an issue with the API and not Moshi
After some trial and error, the current solution for me is as follows:
Create an adapter specific to the data class that may be affected with duplicate fields
object PersonAdapter {
#FromJson
fun fromJson(reader: JsonReader) = with(reader) {
var name: String? = null
var age: Int? = null
beginObject()
while(hasNext()) {
try {
when (reader.nextName()) {
"name" -> name = nextString()
"age" -> age = nextInt()
else -> reader.skipValue()
}
catch (e: Exception) {
//This can happen when getNextString etc is null
reader.skipValue()
}
}
endObject()
Person(name, age)
}
}
Add the adapter to Moshi. This only worked for me when the adapter was added before KotlinJsonAdapterFactory
Moshi.Builder()
.add(PersonAdapter)
.add(KotlinJsonAdapterFactory())
.build()

Handling Errror Response with Moshi

in my Android app, after sending some registration credentials I get the following JSON output from the server:
{
"response":"successfully registered new user",
"email":"testing#gmail.com",
"username":"testing",
"id":9,
"token":"98d26160e624a0b762ccec0cb561df3aeb131ff5"
}
I have modeled this using the Moshi library with the following data class:
#JsonClass(generateAdapter = true)
data class Account (
#Json(name = "id")
val account_id : Long,
#Json(name="email")
val account_email: String,
#Json(name="username")
val account_username: String,
#Json(name="token")
val account_authtoken : String,
#Json(name="response")
val account_response : String
)
Everything works fine. Now I wanted to handle error cases. When I get an error (let's say, the email I want to register with already exists) then I should get a JSON output like this:
// what the app gets when there is some error with the credentials
// e.g. email exists, username exists etc.
{
"error_message" : "The email already exists",
"response": "Error"
}
The method that executes the request looks like the following:
override suspend fun register(email: String, userName: String, password: String, passwordToConfirm: String): NetworkResult<Account> {
// make the request
val response = authApi.register(email, userName, password, passwordToConfirm)
// check if response is successful
return if(response.isSuccessful){
try {
// wrap the response into NetworkResult.Success
// response.body() contains the Account information
NetworkResult.Success(response.body()!!)
}
catch (e: Exception){
NetworkResult.Error(IOException("Error occurred during registration!"))
}
} else {
NetworkResult.Error(IOException("Error occurred during registration!"))
}
}
If the response is successful, then it wraps the response.body() into NetworkResult.Success data class.
My NetworkResult class is a sealed class with two sub data classes Success & Error.
It looks like this:
// I get the idea for this from https://phauer.com/2019/sealed-classes-exceptions-kotlin/
sealed class NetworkResult<out R> {
data class Success<out T>(val data: T) : NetworkResult<T>()
data class Error(val exception: Exception) : NetworkResult<Nothing>()
}
But that does not handle the JSON output for errors I mentioned above. When the app gets the error JSON output, Moshi complains that the Account data class does not have a error_message property which is clear to me because I do not have such a field in my Account data class.
What do I need to change so that I can also handle any error cases I wish ? I know, I could model a second data class and call it Error with the fields response and error_message but my sealed class NetworkResult only accepts one class as generic type.
So, what can I do ?
If you don't initialise a value to a field in data class, Moshi will consider it as a required field.
#JsonClass(generateAdapter = true)
data class Account (
#Json(name = "id")
val account_id : Long = 0,
#Json(name="email")
val account_email: String = "",
#Json(name="username")
val account_username: String = "",
#Json(name="token")
val account_authtoken : String = "",
#Json(name="response")
val account_response : String = "",
#Json(name="error_message")
val error_message : String = ""
)
Like this you can create the same data class for Success and Error both.

Pulling data from an API with okHTTP & GSON

New to using API's (and kotlin for that matter) and I'm having some trouble figuring out how to pull data from an API into model objects and was hoping someone could point me in the right direction. Sample code below.
val request = Request.Builder().header("X-Mashape-Key", keyVal).url(url).build()
//make request client
val client = OkHttpClient()
//create request
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call?, response: Response?) {
//grab as string, works fine
val body = response?.body()?.string()
//make my builder, works fine
val gson = GsonBuilder().create()
// to pass type of class to kotlin ::
val cardFeed = gson.fromJson(body, CardFeed::class.java)
}
override fun onFailure(call: Call?, e: IOException?) {
println("Failed to execute request")
}
})
}
All of that seems to work as intended in debug, I get the string, and can see it but using the following it still dumps a null into the cardFeed/cards object.
class CardFeed(val cards: List<Card>)
class Card(val cardId: String, val name: String, val text: String, val flavor: String)
The body string I'm getting from the API reads as follows
body: "{Basic":[{"key":"value", etc
I'm assuming the [ is what's tripping me up, but not sure how to correct it.
Any ideas or nudges in the correct direction would be greatly appreciated and thanks ahead of time.
According to your class structure, the JSON object that you should get from the API should be
{
"cards":[
{
"cardId":"<cardID>",
"name":"<name>",
"flavor":"<flavor>"
},
{
"cardId":"<cardID>",
"name":"<name>",
"flavor":"<flavor>"
}
]
}
with the correct keys because when you use gson.fromJson(body, CardFeed::class.java), it looks for the variable names in the class assigned (CardFeed.java). If you don't have the correct keys ("cards", "cardId", "name", "flavor"), gson would return null for the values
okHttp is a low level library. In order to consume JSON based APIs, Retrofit is a much more suitable library, as it already have converters that use libraries like GSON or Moshi to do all the heavy lifting for you.

Get Database Row with Anko

I have a Kolin Android Application with a Database. I use the Anko library for the Database Communication. I can insert Data in my Database but I have some troubles when I try to get a data row from my Database.
I try to get the Data Row through the following code
try {
var result = select("projects").where("rowid = {id}",
"id" to 1).parseList(StringParser)
Toast.makeText(applicationContext,result.toString(),Toast.LENGTH_LONG).show()
} catch(e: Exception) {
Toast.makeText(applicationContext,e.toString(),Toast.LENGTH_LONG).show()
}
How can I get the content of the Row with the given id
At the moment I get this error
Invalid row: row for SingleColumnParser must contain exactly one column
example :
data class Controler(val id: Int, var name: String, val ssid: String,val password: String,val serialnumber: String): Serializable {}
and in your activity ->
select("projects", "id", "name", "ssid", "password", "serialnumber")
.whereArgs("(id = {controlerId}) ", "controlerId" to id)
.parseOpt(object : MapRowParser<Controler> {
override fun parseRow(columns: Map<String, Any?>): Controler {
controler = Controler(columns.getValue("id").toString().toInt(),
columns.getValue("name").toString(),
columns.getValue("ssid").toString(),
columns.getValue("password").toString(),
columns.getValue("serialnumber").toString())
return controler as Controler
}
})
Hi you must share the complete code, and the log, what message show?
Try this. Why don't use "_id" ? Sqlite required.
select("projects")
.where("(_id = {id}),
"id" to 1)

Twitter API 401's on /account/verify_credentials

I am trying to make use of the twitter api, and am setting up a handler to deal with twitter api requests.
To do this I am using an HTTPUrlConnection and following the Twitter api docs
I've managed to get authenticated using the 3-legged OAuth process, but am stuck when actually trying to make requests with the oauth token.
Here is an example of what my auth headers look like:
Accept=*/*,
Connection=close,
User-Agent=OAuth gem v0.4.4,
Content-Type=application/x-www-form-urlencoded,
Authorization=
OAuth oauth_consumer_key=****&
oauth_nonce=bbmthpoiuq&
oauth_signature=*****%3D&
oauth_signature_method=HMAC-SHA1&
oauth_timestamp=1570586135&
oauth_token=*****&
oauth_version=1.0,
Host=api.twitter.com
for each header in the auth header I add it to my HTTP GET call like this:
urlConnection.setRequestProperty(header.key, header.value)
Note that Authorization points to one string
OAuth oauth_consumer_key=****&oauth_nonce=bbmthpoiuq&oauth_signature=*****%3Doauth_signature_method=HMAC-SHA1oauth_timestamp=1570586135&oauth_token=*****&oauth_version=1.0,Host=api.twitter.com
The following params are collected as follows:
oauth_consumer_key is my application API key
oauth_signature is computed by the hmacSign function below
oauth_token is the "oauth_token" received in the response from /oauth/access_token
The hmacSign function:
private fun hmacSign(requestType: RequestType, url: String, params: Map<String, String>): String {
val type = "HmacSHA1"
val key = "$API_SECRET&$tokenSecret"
val value = makeURLSafe("${requestType.string}&$url${getURLString(params.toList().sortedBy { it.first }.toMap())}")
val mac = javax.crypto.Mac.getInstance(type)
val secret = javax.crypto.spec.SecretKeySpec(key.toByteArray(), type)
mac.init(secret)
val digest = mac.doFinal(value.toByteArray())
return makeURLSafe(Base64.encodeToString(digest, NO_WRAP))
}
private fun makeURLSafe(url: String) : String {
return url.replace("/", "%2F")
.replace(",", "%2C")
.replace("=", "%3D")
.replace(":", "%3A")
.replace(" ", "%2520")
}
protected fun getURLString(params: Map<String, Any>) : String {
if (params.isEmpty()) return ""
return params.toList().fold("?") { total, current ->
var updated = total
updated += if (total == "?")
"${current.first}=${current.second}"
else
"&${current.first}=${current.second}"
updated
}
}
In the GET call I'm referring to, tokenSecret would be the oauth secret received from /oauth/access_token
After i make the call I get a 400: Bad Request
Is there anything obvious I'm doing wrong?
Update: By putting the params at the end of the url like ?key=value&key2=value2... instead of a 400 I get a 401. So I'm not sure which is worse, or which is the right way to do it.
Okay, finally got it working
So using the suggestion in the comments, I downloaded postman and copied all my info into postman - when i made the request there, I got a 200!
So then i looked back and tried to figure out what was different and there were a few things:
First the hmac sign function was missing an &
New function (added another & after the url):
private fun hmacSign(requestType: RequestType, url: String, params: Map<String, String>): String {
val type = "HmacSHA1"
val key = "$API_SECRET&$tokenSecret"
val value = makeURLSafe("${requestType.string}&$url&${getURLString(params.toList().sortedBy { it.first }.toMap())}")
val mac = javax.crypto.Mac.getInstance(type)
val secret = javax.crypto.spec.SecretKeySpec(key.toByteArray(), type)
mac.init(secret)
val digest = mac.doFinal(value.toByteArray())
return makeURLSafe(Base64.encodeToString(digest, NO_WRAP))
}
Next I noticed my auth header had its params seperated with & but they all should've been replaced with , - i also needed to surround each of my values in ""
So it looked like:
OAuth oauth_consumer_key="****",oauth_nonce="bbmthpoiuq",oauth_signature="*****%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1570586135",oauth_token="*****",oauth_version="1.0",Host="api.twitter.com"
After these changes i started getting 200!
Hopefully this helps someone else (though im sure its unlikely considering how specific these issues were)

Categories

Resources