Razorpay callbacks is not working in fragment instead of activity using fragment please give a solution If anyone aware thanks in advance.
private fun startPayment() {
val activity: Activity = requireActivity()
val co = Checkout()
try {
val options = JSONObject()
options.put("name", "Vendor")
options.put("description", " for Order")
//You can omit the image option to fetch the image from dashboard
options.put("image", "https://rzp-mobile.s3.amazonaws.com/images/rzp.png")
options.put("currency", "INR")
val payment: String = "1"//getcart?.CartTotal.toString()
// amount is in paise so please multiple it by 100
//Payment failed Invalid amount (should be passed in integer paise. Minimum value is 100 paise, i.e. ₹ 1)
var total = payment.toDouble()
total = total * 100
options.put("amount", total)
val preFill = JSONObject()
preFill.put("email", "hell#gmail.com")
preFill.put("contact", "9898989898")
options.put("prefill", preFill)
co.open(requireActivity(), options)
} catch (e: Exception) {
Toast.makeText(activity, "Error in payment: " + e.message, Toast.LENGTH_SHORT).show()
e.printStackTrace()
}
}
override fun onPaymentSuccess(s: String?) {
toast("onPaymentSuccess")
Log.i(TAG, "onPaymentSuccess: $s")
}
override fun onPaymentError(i: Int, s: String?) {
Log.e(TAG, "error code "+i.toString()+" -- Payment failed "+s.toString())
try {
toast("Payment error please try again")
} catch (e : Exception) {
Log.e("OnPaymentError", "Exception in onPaymentError", e);
}
}
Amount should be in Integer and in paise.
implement result listener to the fragment host activity and override error and success function there.
in fragment you won't get Razorpay callback function, so you should implement PaymentResultListener or PaymentResultWithDataListener in activity and from activity you have call your fragment and do your api call for razor pay response.
in your Activity:
#Override
public void onPaymentSuccess(String s, PaymentData paymentData) {
try {
FeeFragment feeList = (FeeFragment) mViewPager.getAdapter().instantiateItem(mViewPager, mViewPager.getCurrentItem());
feeList.checkRazorResponse(paymentData, true);
} catch (Exception e) {
Log.e("Exception in success", e.toString());
e.printStackTrace();
}
}
in your Fragment:
public void checkRazorResponse(PaymentData paymentData, boolean success) {
if (success) {
updatePaymentStatus(paymentData);
//do your api call
} else {
//handle error message
}
}
Razorpay payment integration is not supported on the fragment. You have to implement it on Activity.
Like this way
This code is for fragment:
binding.tvPlaceOrder.setOnClickListener(view -> {
startActivity(new Intent(getActivity(), PaymentActivity.class));
}
}
});
This code is for Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPaymentBinding.inflate(layoutInflater)
setContentView(binding.root)
payOnline()
}
private fun payOnline() {
val checkout = Checkout()
checkout.setKeyID(getString(R.string.razorpay_api_key))
checkout.setImage(R.mipmap.ic_launcher)
try {
val options = JSONObject()
options.put("name", "mName")
options.put("currency", "INR")
options.put("image", R.mipmap.ic_launcher)
options.put("amount", 10000) //pass amount in currency subunits
options.put("prefill.email", "roydeveloper01#gmail.com")
options.put("prefill.contact", "8620828385")
checkout.open(this, options)
} catch (e: Exception) {
Toast.makeText(this, "Error in starting Razorpay Checkout: $e", Toast.LENGTH_LONG).show()
}
}
override fun onPaymentSuccess(p0: String?) {
try {
binding.tvId.text = "Payment Successful \n Transaction ID $p0"
} catch (e: NoSuchAlgorithmException) {
binding.tvId.text ="Exception"
}
}
override fun onPaymentError(p0: Int, p1: String?) {
try {
binding.tvId.text ="Exception: $p1"
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
}
binding.tvId.text ="Error: $p1"
}
My problem is solved this way. I think your problem will also solve . Best of luck.
Related
i am investigating Google Identity Services in my current Android project for Sign In With Google.
theres this code in the docs:-
private static final int REQUEST_CODE_GOOGLE_SIGN_IN = 1; /* unique request id */
private void signIn() {
GetSignInIntentRequest request =
GetSignInIntentRequest.builder()
.setServerClientId(getString(R.string.server_client_id))
.build();
Identity.getSignInClient(activity)
.getSignInIntent(request)
.addOnSuccessListener(
result -> {
try {
startIntentSenderForResult(
result.getIntentSender(),
REQUEST_CODE_GOOGLE_SIGN_IN,
/* fillInIntent= */ null,
/* flagsMask= */ 0,
/* flagsValue= */ 0,
/* extraFlags= */ 0,
/* options= */ null);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "Google Sign-in failed");
}
})
.addOnFailureListener(
e -> {
Log.e(TAG, "Google Sign-in failed", e);
});
}
however startIntentSenderForResult method is marked ad deprecated with the following comment:-
This method has been deprecated in favour of using the Activity Result API which brings increased type
safety via an ActivityResultContract and the prebuilt contracts for common intents available in
androidx.activity.result.contract.ActivityResultContracts, provides hooks for testing, and allow
receiving results in separate, testable classes independent from your activity.
Use registerForActivityResult(ActivityResultContract, ActivityResultCallback) passing
in a StartIntentSenderForResult object for the ActivityResultContract.
i do not understand how to replace startIntentSenderForResult with registerForActivityResult(ActivityResultContract, ActivityResultCallback), when i follow the instructions and pass the StartIntentSenderForResult object for the ActivityResultContract as follows i get a compile error message
heres my code
private fun signIn() {
val request: GetSignInIntentRequest = GetSignInIntentRequest.builder()
.setServerClientId(getString(R.string.server_client_id))
.build()
Identity.getSignInClient(this#MainActivity)
.getSignInIntent(request)
.addOnSuccessListener {
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
Log.d(TAG, "signIn() called ${it.data}")
Log.d(TAG, "signIn() called ${it.resultCode}")
}
}
.addOnFailureListener { e -> Log.e(TAG, "Google Sign-in failed", e) }
}
this is what i ended up with:-
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeGoogleSignIn()
}
private val launcher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult(), ::handleSignInResult)
private fun handleSignInResult(result: ActivityResult) {
val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(result.data)
try {
val account = task.getResult(ApiException::class.java)
Log.d(TAG, "handleSignInResult() called with: result = $account")
} catch (ex: ApiException) {
Log.e(TAG, "handleSignInResult: ", ex)
}
}
private fun initializeGoogleSignIn() {
val request = GetSignInIntentRequest.builder()
.setServerClientId(getString(R.string.server_client_id))
.build()
Identity.getSignInClient(this)
.getSignInIntent(request)
.addOnSuccessListener { result ->
val intentSenderRequest = IntentSenderRequest.Builder(result).build()
launcher.launch(intentSenderRequest)
}
.addOnFailureListener { e ->
Log.e(TAG, "initializeGoogleSignIn: ",e )
}
}
}
I am justing trying to get phone number using GetPhoneNumberHintIntentRequest to replace HintRequest.
So just trying to follow google developer doc https://developers.google.com/identity/phone-number-hint/android#kotlin_2. But after following doc I feel this doc is incomplete.
val phoneNumberHintIntentResultLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
try {
val phoneNumber = Identity.getSignInClient(requireActivity()).getPhoneNumberFromIntent(result.data)
} catch(e: Exception) {
}
}
So as per doc you need to pass intent to phoneNumberHintIntentResultLauncher but there is no method inside GetPhoneNumberHintIntentRequest.
Even if you see doc then you realise that you need to replace signInClient to getSignInClient.
If any one know about above issue then let me know or any doc where I can achieve my goal.
Have been facing this recently.
Please change the result launcher type as follows.
val resultLauncher: ActivityResultLauncher<IntentSenderRequest> = registerForActivityResult(StartIntentSenderForResult()) { result ->
try {
val phoneNumber = Identity.getSignInClient(requireActivity()).getPhoneNumberFromIntent(result.data)
// Do something with the number
} catch (e: Exception) {
Log.e(TAG, "Phone Number Hint failed")
}
And launch the intent as
...
.addOnSuccessListener { request: PendingIntent ->
try {
resultLauncher.launch(IntentSenderRequest.Builder(request).build())
} catch(e: Exception) {
Log.e(TAG, "Launching the PendingIntent failed")
}
}
...
The document is indeed incomplete as it seems.
// initialise above onCreate
private ActivityResultLauncher<IntentSenderRequest> someActivityResultLauncher;
// declare in onCreate
private void phoneSelection() {
GetPhoneNumberHintIntentRequest request = GetPhoneNumberHintIntentRequest.builder().build();
Identity.getSignInClient(RegistrationActivity.this)
.getPhoneNumberHintIntent(request)
.addOnFailureListener(e -> {
Toast.makeText(activity, "Error : "+e.getMessage(), Toast.LENGTH_SHORT).show();
}).addOnSuccessListener(pendingIntent -> {
IntentSenderRequest intentSenderRequest = new IntentSenderRequest.Builder(pendingIntent.getIntentSender()).build();
someActivityResultLauncher.launch(intentSenderRequest);
});
}
// declare in onCreate
private void resultLauncher() {
someActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(), result -> {
try {
String phoneNumber = Identity.getSignInClient(RegistrationActivity.this).getPhoneNumberFromIntent(result.getData());
binding.editNumber.setText(phoneNumber.substring(3)); //get the selected phone
} catch (ApiException e) {
e.printStackTrace();
Toast.makeText(activity, "Error : "+ e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
I have the following setup
Service
// ItunesService
suspend fun searchItunesPodcast(#Query("term") term: String): Response<PodcastResponse>
Repository
// ItunesRepo
override suspend fun searchByTerm(term: String) = withContext(ioDispatcher) {
return#withContext itunesService.searchItunesPodcast(term)
}
ViewModel
fun searchPodcasts(term: String) {
viewModelScope.launch {
_res.value = Result.loading()
try {
val response = itunesRepo.searchByTerm(term)
if (response.isSuccessful) { // Nothing from here when no internet
_res.value = Result.success(response.body())
} else {
_res.value = Result.error(response.errorBody().toString())
}
} catch (e: Exception) {
_res.value = Result.exception(e)
}
}
}
Everything works great until i turn off mobile data/internet on my testing device. _res value stuck on Loading state. I have tried adding break point at if (response.isSuccessful) when there is no internet and it seams like val response = itunesRepo.searchByTerm(term) never returns how can I fix this
I switched to using Flow api on my Repository
override suspend fun searchPodcasts(term: String) = flow {
emit(Result.Loading)
try {
val res = itunesService.searchItunesPodcast(term)
if (res.isSuccessful)
emit(Result.Success(res.body()))
else
emit(Result.Error("Generic error: ${res.code()}"))
} catch (e: Exception) {
emit(Result.Error("Unexpected error", e))
}
}.flowOn(ioDispatcher)
Then collect the results on my ViewModels
I'm new to coroutines and having a hard time figuring out how to correctly wrap an existing callback in a coroutine.
My goal is to be able to do the following:
lifecycleScope.launch {
withContext(Dispatchers.Main) {
val theResult = getPreRollAd() //1. call this suspending func and wait for result
doSomethingWithResult(theResult) //2. now that the result is returned from AdsWizz API (below), do something with it
}
}
Here is the AdsWizz API call that I'd like to "wrap":
val adReqInterface: AdRequestHandlerInterface = object :
AdRequestHandlerInterface {
override fun onResponseError(error: AdswizzSDKError) {
Timber.e("onResponseError $error")
}
override fun onResponseReady(adResponse: AdResponse) {
Timber.d( "onResponseReadySingleAd")
//this contains the url to the ad, title, etc..
!!!*** I WANT TO RETURN THE adResponse.mediaFile?.source string back to "theResult" variable above (in lifecycleScope.launch {.... )
}
}
try {
AdswizzSDK.getAdsLoader().requestAd(adReqParams, adReqInterface)
} catch (e: IllegalArgumentException) {
Timber.d( "IllegalArgumentException")
} catch (e: SecurityException) {
Timber.d( "SecurityException")
} catch (e: Exception) {
Timber.d( "other exception")
e.printStackTrace()
}
I've tried using suspendCoroutine {... to wrap but nothing is working. Really appreciate someones help re the right way to achieve this.
the right way to do it is to use suspendCancellableCoroutine. It can return a result or can be cancelled with an exception.
suspend fun getPreRollAd(): AdResponse {
return suspendCancellableCoroutine {
...
val adReqInterface: AdRequestHandlerInterface = object : AdRequestHandlerInterface {
override fun onResponseError(error: AdswizzSDKError) {
Timber.e("onResponseError $error")
it.cancel(error)
}
override fun onResponseReady(adResponse: AdResponse) {
Timber.d( "onResponseReadySingleAd")
it.resume(adResponse)
}
}
AdswizzSDK.getAdsLoader().requestAd(adReqParams, adReqInterface)
}
}
viewModelScope.launch {
val result = try {
getPreRollAd()
} catch(e: Throwable) {
null
}
...
}
I'm using retrofit 2.6 with kotlin coroutines to make API call without block the UI thread, I got it work but the app crashes when I switch off the internet connection. The logcat error is: E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Here is my code:
private fun handleIntent(slug: String) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
} catch (e: HttpException) {
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong", Toast.LENGTH_LONG)
}
}
}
I've got the code working, the new code is:
private fun handleIntent(slug: String) = GlobalScope.launch(Dispatchers.Main) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
try {
val res = withContext(Dispatchers.IO) {
service.getPostBySlug(slug)
}
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
catch (e: HttpException) {
Toast.makeText(this#PostContentActivity, "Exception ${e.message}", Toast.LENGTH_LONG).show()
}catch (e: IOException) {
UtilityMethods.hideView(loading_view)
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong ${e.message}", Toast.LENGTH_LONG).show()
}
}
So while looking into stacktrace I found that ConnectException is thrown when network is unavailable
And that's how I do it in kotlin and it works for me,
suspend fun<T: Any> safeAPICall(call: suspend () -> Response<T>) : T{
val response = try {
call.invoke()
}
catch (e:java.lang.Exception){
e.printStackTrace()
val message = if( e is ConnectException) "Connection Error" else "Something went wrong. Please try again."
throw IOException(ResponseError(message, 500).convertToJsonString())
}
// When connection is OK
if(response.isSuccessful){
return response.body()!!
}else{
val error = response.errorBody()?.string()
error?.let{
val message = JSONObject(it).optString("message", "Something went wrong")
val responseError = ResponseError(message, response.code())
throw IOException(responseError.convertToJsonString())
}
throw IOException(ResponseError("Something went wrong. Please try again.", 500).convertToJsonString())
}
}
The data class that I use
data class ResponseError(val message:String, val errorCode:Int)
Usage:
try {
val response = safeAPICall {APIClient.planner.viewSites(view.context.authToken)}
}
catch (e:Exception){
view.snack(e.message?.toModel<ResponseError>()?.message?: unspecified_error)
}
Bonus:
inline fun <reified T> JSONObject.toModel(): T? = this.run {
try {
Gson().fromJson<T>(this.toString(), T::class.java)
}
catch (e:java.lang.Exception){ e.printStackTrace(); null }
}
inline fun <reified T> String.toModel(): T? = this.run {
try {
JSONObject(this).toModel<T>()
}
catch (e:java.lang.Exception){ null }
}
Instead of this:
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
Try this one:
CoroutineScope(Dispatchers.Main).launch {
val res = service.getPostBySlug(slug)
withContext(Dispatchers.IO) {
try {
wrap your 'try and catch' block code within Dispatchers.IO instead of wraping your Dispatchers.IO with in yout try block