Having trouble implementing Braintree payment for Android using Firebase as backend - android

I get this output on firebase: "Cannot determine payment method"
Note: I don't have too much knowledge on JavaScript and Firebase functions and can't get this to work.
I have been following Braintree's guide to setting up the payment method. This is the flow of events:
Step 1
Your front-end requests a client token from your server and initializes the client SDK.
Step 2
Your server generates and sends a client token back to your client using the server SDK.
Step 3
The customer submits payment information, the client SDK communicates that information to Braintree and returns a payment method nonce.
Step 4
Your front-end sends the payment method nonce to your server.
Step 5
Your server code receives the payment method nonce and then uses the server SDK to create a transaction.
So far I've been stuck at step 5. The nonce gets posted to my Firebase database in the following format:
-nonce
-LsHNQLklFEX48oiSt1H:"tokencc_bc_vbmypn_dvn4gy_xy47fw_th6vv9_bq6"
-LsHn2hilKlmVeT1g1Ow: tokencc_bf_8qrjsk_j7bg44_p8kk88_qz7vn2_vk6"
How would i go about step 5?
Functions code:
'use strict';
const functions = require('firebase-functions');
const express = require('express');
const app = express();
const braintree = require('braintree');
const admin = require('firebase-admin');
admin.initializeApp();
var clientToken;
var nonceFromTheClient;
var transaction;
var gateway = braintree.connect({
environment: braintree.Environment.Sandbox,
merchantId:"",
publicKey: "",
privateKey: ""
});
exports.createToken = functions.https.onRequest((request, response) => {
gateway.clientToken.generate({}, function (err, response) {
if (err){
console.log('inside error',err);
} else {
clientToken = response.clientToken;
}
});
response.send(clientToken);
});
exports.createTransaction = functions.https.onRequest((req, res) => {
app.post("/nonce", function (req, res) {
nonceFromTheClient = req.body.payment_method_nonce;
console.log("nonce" + nonceFromTheClient)
});
gateway.transaction.sale({amount: '10.00', paymentMethodNonce: nonceFromTheClient, options: {submitForSettlement: true}}, function (err, result) {
if (err) {
console.error(err);
return;
}
if (result.success) {
console.log('Transaction status: ' + result.transaction.status);
} else {
console.error(result.message);
}
});
res.send(transaction);
});
Activity code:
if (cardPayment.isChecked) {
val client = AsyncHttpClient()
client.get(
"https://.../createToken",
object : TextHttpResponseHandler() {
override fun onFailure(
statusCode: Int,
headers: Array<out Header>?,
responseString: String?,
throwable: Throwable?
) {
}
override fun onSuccess(
statusCode: Int,
headers: Array<Header>,
clientToken: String
) {
mClientToken = clientToken
onBraintreeSubmit(it)
}
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE && data != null) {
if (resultCode == Activity.RESULT_OK) {
val result =
data.getParcelableExtra<DropInResult>(DropInResult.EXTRA_DROP_IN_RESULT)
val nonce = result.paymentMethodNonce?.nonce
if (nonce != null) {
FirebaseShoppingCartDatabase.postNonceToDatabase(nonce)
val client = AsyncHttpClient()
client.get(
"https://..../createTransaction",
object : TextHttpResponseHandler() {
override fun onFailure(
statusCode: Int,
headers: Array<out Header>?,
responseString: String?,
throwable: Throwable?
) {
}
override fun onSuccess(
statusCode: Int,
headers: Array<Header>,
clientToken: String
) {
}
})
}
} else if (resultCode == RESULT_CANCELED) {
} else {
// handle errors here, an exception may be available in
val error = data.getSerializableExtra(DropInActivity.EXTRA_ERROR) as Exception
System.out.println("eroare " + error)
}
}
}
fun onBraintreeSubmit(v: View) {
val dropInRequest = DropInRequest()
.clientToken(mClientToken)
dropInRequest
.disablePayPal()
startActivityForResult(dropInRequest.getIntent(context), REQUEST_CODE)
}
}

Got it to work. I was passing the nonce to the server incorrectly. (using a different URL)

Related

Find a value in Kotlin Data Class

i'm actually new to Kotlin android development. I'm making an app that uses Google sheets as a database. My app can successfully run after Google sign in. In fact I want my user to sign in to app, if their email ID is present in the Emails sheet in the Google sheet. So I have done following steps in my code so far.
Sign in user with Google Sign In
Retrieve data from "Emails" sheet in my Google Spreadsheet
Then store them in to a data class (Customers data class in Shipment.kt)
Then I check whether signed in user's email ID available in the data class or not. This is where I need help. It's giving me this error
"Type inference failed. The value of the type parameter T should be mentioned in input types (argument types, receiver type or expected type). Try to specify it explicitly."
Can someone help me on this?
Below is my data class in Shipment.kt
package com.example.demoonlinesheet
data class Shipments(
val shipperName:String,
val volume:String,
val eta:String,
val etd:String)
data class Customers(
val companyName:String,
val customerName:String,
val emailID:String)
Below is the code that I have written so far in MainActivity.kt
const val RC_SIGN_IN = 123
const val EXTRA_MESSAGE = "com.example.demoonlinesheet.MESSAGE"
const val EXTRA_MESSAGE2 = "com.example.demoonlinesheet.MESSAGE"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Configure sign-in to request the user's ID, email address, and basic
// profile. ID and basic profile are included in DEFAULT_SIGN_IN.
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build()
// Build a GoogleSignInClient with the options specified by gso.
var mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
sign_in_button.setOnClickListener{
val signInIntent = mGoogleSignInClient.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}
val acct = GoogleSignIn.getLastSignedInAccount(this)
if (acct != null) {
startActivity(Intent(this, SecondActivity::class.java).apply{
putExtra(EXTRA_MESSAGE, acct.displayName)
//putExtra(EXTRA_MESSAGE2, acct.email)
} )
}
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
if (requestCode == RC_SIGN_IN) {
// The Task returned from this call is always completed, no need to attach
// a listener.
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
handleSignInResult(task)
}
}
private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
val companyList= arrayListOf<Customers>()
val url="https://sheets.googleapis.com/v4/spreadsheets/{sheetID}/values/{sheetName}?alt=json&key={APIKey}"
val queue = Volley.newRequestQueue(this)
val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
{
fun onResponse(response: JSONObject) {
try {
// val feedObj = response.getJSONObject("")
val entryArray = response.getJSONArray("values")
for (i in 2 until entryArray.length()) {
val entryObj = entryArray.getJSONArray(i)
val companyName = entryObj[0].toString()
val customerName = entryObj[1].toString()
val emailID = entryObj[2].toString()
// entryObj.getJSONObject("gsx\$lastname").getString("\$t")
companyList.add(Customers(companyName, customerName, emailID))
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
}, {
fun onErrorResponse(error: VolleyError?) {
Toast.makeText(this#MainActivity, "Fail to get data..", Toast.LENGTH_SHORT)
.show()
}
})
queue.add(jsonObjectRequest)
fun checkUser() {
val getAccount = completedTask.getResult(ApiException::class.java)
val emailLoggedIn = getAccount.email.toString()
val companies = listOf<Customers>()
//this is where I get the error message "Type inference failed. The value of the type parameter T should be mentioned in input types (argument types, receiver type or expected type). Try to specify it explicitly."
if (emailLoggedIn in companies){
//do something here
}
try {
val account = completedTask.getResult(ApiException::class.java)
val loggedname = account.displayName
//startActivity(Intent(this#MainActivity, SecondActivity::class.java))
val intent = Intent(this, SecondActivity::class.java).apply {
putExtra(EXTRA_MESSAGE, loggedname)
}
startActivity(intent)
} catch (e: ApiException) {
}
}
}
From your code:
if (emailLoggedIn in companies) {
//do something here
}
emailLoggedIn is a String, companies is a List<Customers>.
How does List know how to compare String and Customers? :)
You need something like this:
if (companies.any { it.emailID == emailLoggedIn }){
//do something here
}
I should also mention that you can leave the if condition unchanged and add the following code that overloads the in keyword:
operator fun List<Customers>.contains(email: String): Boolean {
return this.any { it.emailID == email }
}
But, in my opinion, this overload looks terrible and confusing :)

Spotify AUTHENTICATION SERVICE UNKNOWN_ERROR kotlin android

I am trying authenticate user with spotify app and spotify auth API (implementation 'com.spotify.android:auth:2.0.1') followed the steps mentioned in Spotify SDK github sample
my code:
Added in gradle(app.module) defaultConfig { manifestPlaceholders = [redirectSchemeName: "appname", redirectHostName:"spotify_login_callback"] }
// Fragment/Activity
val CLIENT_ID = "7bf56252cd644b339cc97df5b4d7eeee"
val AUTH_TOKEN_REQUEST_CODE = 0x10
val AUTH_CODE_REQUEST_CODE = 0x11
var mAccessToken: String? = null
var mAccessCode: String? = null
fun onRequestTokenClicked() {
val request = getAuthenticationRequest(AuthorizationResponse.Type.TOKEN)
AuthorizationClient.openLoginActivity(requireActivity(), AUTH_TOKEN_REQUEST_CODE, request)
}
fun onRequestCodeClicked() {
val request: AuthorizationRequest =
getAuthenticationRequest(AuthorizationResponse.Type.CODE)
AuthorizationClient.openLoginActivity(requireActivity(), AUTH_CODE_REQUEST_CODE, request)
}
private fun getAuthenticationRequest(type: AuthorizationResponse.Type): AuthorizationRequest {
return AuthorizationRequest.Builder(
CLIENT_ID,
type,
getRedirectUri().toString()
)
.setShowDialog(false)
// "user-read-email"
.setScopes(arrayOf("user-read-email")) // user-read-private , "streaming"
.build()
}
private fun getRedirectUri(): Uri? {
return Uri.Builder()
.scheme("appname")
.authority("spotify_login_callback")
.build()
}
val response = AuthorizationClient.getResponse(resultCode, data)
if (response.error != null && !response.error.isEmpty()) {
setResponse(response.error)
Toast.makeText(requireActivity(),"Error: response.error"+response.error,Toast.LENGTH_SHORT).show()
}
if (requestCode == AUTH_TOKEN_REQUEST_CODE) {
mAccessToken = response.accessToken
Toast.makeText(requireActivity(),"AccessToken: "+mAccessToken,Toast.LENGTH_SHORT).show()
updateTokenView()
} else if (requestCode == AUTH_CODE_REQUEST_CODE) {
mAccessCode = response.code
Toast.makeText(requireActivity(),"AccessCode"+mAccessCode,Toast.LENGTH_SHORT).show()
}
This code prints log "Spotify auth completing. The response is in EXTRA with key response" after debugging library gives AUTHENTICATION SERVICE UNKNOWN_ERROR does anyone know the cause of this error, same code provided in SDK sample works fine.

Using Coroutine runblock with the Authenticator to handle 401 response from retrofit

I am trying to use the Authenticator to handle 401 response. What I have done is
fun provideAccessTokenAuthenticator(
mainApiServiceHolder: MainApiServiceHolder,
preferences: SharedPreferences
) = object : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
val accessToken = preferences.getString(ACCESS_TOKEN, null)
if (!isRequestWithAccessToken(response) || accessToken == null) {
return null
}
synchronized(this) {
val newAccessToken = preferences.getString(ACCESS_TOKEN, null)!!
// Access token is refreshed in another thread.
if (accessToken != newAccessToken) {
return newRequestWithAccessToken(response.request, newAccessToken)
}
// Need to refresh an access token
val refreshTokenResponse = runBlocking {
Log.d("zzzzzzzzzz", "refresh token is running")
mainApiServiceHolder.mainApiService?.refreshToken(
"refresh_token",
preferences.getString(REFRESH_TOKEN, null)!!,
AuthRepository.CLIENT_ID,
AuthRepository.CLIENT_SECRET
)
}
Log.d("zzzzzzzzzz", refreshTokenResponse?.body()?.access_token!!)
return if (refreshTokenResponse?.isSuccessful!!) {
Log.d("zzzzzzzzzz", "refresh token is successful")
newRequestWithAccessToken(
response.request,
refreshTokenResponse.body()?.access_token!!
)
} else {
Log.d("zzzzzzzzzz", "refresh token is unsuccessful")
response.request.newBuilder().header("Content-Type", "application/json").build()
}
}
}
Now, it gets called when there is a 401 response. The refresh token call is also fired (from Log). However, it never gets the result in the refreshTokenResponse and nothing happens after that. I think its a wrong way of using runBlock. The api is
#FormUrlEncoded
#POST("/api/auth/token/")
suspend fun refreshToken(
#Field("grant_type") grant_type: String,
#Field("refresh_token") refresh_token: String,
#Field("client_id") client_id: String,
#Field("client_secret") client_secret: String
): Response<LoginResponse>
Any help would be really appreciated. Thanks
In the Retrofit API, consider replacing your async runBlocking{} suspend fun with a synchronous Call. I had the most luck avoiding the use of coroutines inside the Authenticator.
I was having the same problem. The token request went straight into a black hole. The app froze. The request was never seen again. No error, no nothing.
But everywhere else in the app, the suspend fun came back just fine. From ViewModels, from WorkManager, it worked every time. But from the Authenticator, never. What was wrong with the Authenticator? What was special about the Authenticator that made it act this way?
Then I replaced the runBlocking{} coroutine with a straightforward Call. This time, the request came back and the token arrived without a fuss.
The way I got the API to work looked like this:
#FormUrlEncoded
#POST("token")
fun refreshTokenSync(
#Field("refresh_token") refreshToken: String,
): Call<RefreshMyTokenResponse>
Then, in the Authenticator:
val call = API.refreshTokenSync(refreshToken)
val response = call.execute().body()
I hope this helps someone else who ran into the same issue. You may receive a warning from Android Studio that this is an inappropriate blocking call. Ignore it.
Refresh token only once for multiple requests
Log out user if refreshToken failed
Log out if user gets an error after first refreshing
Queue all requests while token is being refreshed
https://github.com/hoc081098/Refresh-Token-Sample/blob/master/app/src/main/java/com/hoc081098/refreshtokensample/data/remote/interceptor/AuthInterceptor.kt
class AuthInterceptor #Inject constructor(
private val userLocalSource: UserLocalSource,
private val apiService: Provider<ApiService>,
) : Interceptor {
private val mutex = Mutex()
override fun intercept(chain: Interceptor.Chain): Response {
val req = chain.request().also { Timber.d("[1] $it") }
if (NO_AUTH in req.headers.values(CUSTOM_HEADER)) {
return chain.proceedWithToken(req, null)
}
val token =
runBlocking { userLocalSource.user().first() }?.token.also { Timber.d("[2] $req $it") }
val res = chain.proceedWithToken(req, token)
if (res.code != HTTP_UNAUTHORIZED || token == null) {
return res
}
Timber.d("[3] $req")
val newToken: String? = runBlocking {
mutex.withLock {
val user =
userLocalSource.user().first().also { Timber.d("[4] $req $it") }
val maybeUpdatedToken = user?.token
when {
user == null || maybeUpdatedToken == null -> null.also { Timber.d("[5-1] $req") } // already logged out!
maybeUpdatedToken != token -> maybeUpdatedToken.also { Timber.d("[5-2] $req") } // refreshed by another request
else -> {
Timber.d("[5-3] $req")
val refreshTokenRes =
apiService.get().refreshToken(RefreshTokenBody(user.refreshToken, user.username))
.also {
Timber.d("[6] $req $it")
}
val code = refreshTokenRes.code()
if (code == HTTP_OK) {
refreshTokenRes.body()?.token?.also {
Timber.d("[7-1] $req")
userLocalSource.save(
user.toBuilder()
.setToken(it)
.build()
)
}
} else if (code == HTTP_UNAUTHORIZED) {
Timber.d("[7-2] $req")
userLocalSource.save(null)
null
} else {
Timber.d("[7-3] $req")
null
}
}
}
}
}
return if (newToken !== null) chain.proceedWithToken(req, newToken) else res
}
private fun Interceptor.Chain.proceedWithToken(req: Request, token: String?): Response =
req.newBuilder()
.apply {
if (token !== null) {
addHeader("Authorization", "Bearer $token")
}
}
.removeHeader(CUSTOM_HEADER)
.build()
.let(::proceed)
}

InvalidRequestException Invalid Client Secret - Stripe payment sdk

I'm trying to accept payment but getting this error in callback of sdk.
Code:
val params = cardInputWidget.paymentMethodCreateParams
if (params != null) {
val confirmParams =
ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams(params, clientSecret)
stripe = Stripe(
applicationContext,
PaymentConfiguration.getInstance(applicationContext).publishableKey)
stripe.confirmPayment(this, confirmParams)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
stripe.onPaymentResult(requestCode, data, object : ApiResultCallback<PaymentIntentResult> {
override fun onSuccess(result: PaymentIntentResult) {
val paymentIntent = result.intent
val status = paymentIntent.status
if (status == StripeIntent.Status.Succeeded) {
val gson = GsonBuilder().setPrettyPrinting().create()
showToast("Payment succeeded " + gson.toJson(paymentIntent))
} else {
showToast("Payment Error: "+paymentIntent.lastPaymentError?.message ?: "")
}
}
override fun onError(e: Exception) {
showToast("Payment failed "+e.message)
}
})
}
onError is getting always called!
This is internal code of sdk:
You are using confirmPayment method instead of confirmSetupIntent method. One is used for confirming payment intents with client secret, and the other one is used for confirming setup intents with client secret. Checkout the stripe documentation for saving and reusing payment methods: https://stripe.com/docs/payments/save-and-reuse

Android: AWS Cognito in combination with API Gateway using wrong credentials

I'm trying to use AWS Cognito user pools in combination with the AWS API Gateway.
Logging in works fine, when entering the credentials the success handler is called and I can see the credentials. When verifying the JWT token at jwt.io I can also see that the user is correct.
However, when calling the API Gateway using the ApiClientFactory I always receive an error: com.amazonaws.mobileconnectors.apigateway.ApiClientException: Basic (classic) flow is not supported with RoleMappings, please use enhanced flow. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: InvalidParameterException; Request ID: 1a61f1fd-91d8-11e8-82bc-675071b1c307) (Service: null; Status Code: 0; Error Code: null; Request ID: null)
Please see my code below:
Main activity:
AWSMobileClient.getInstance().initialize(this) {
// Obtain the reference to the AWSCredentialsProvider and AWSConfiguration objects
// Use IdentityManager#getUserID to fetch the identity id.
IdentityManager.getDefaultIdentityManager().getUserID(object : IdentityHandler {
override fun onIdentityId(identityId: String) {
Log.d("MainActivity", "Identity ID = " + identityId)
}
override fun handleError(exception: Exception) {
Log.d("MainActivity", "Error in retrieving the identity" + exception)
}
})
}.execute()
LoginFragment:
val authenticationHandler = object : AuthenticationHandler {
override fun getAuthenticationDetails(continuation: AuthenticationContinuation, userID: String) {
val authDetails = AuthenticationDetails(inputUsername.text.toString(), inputPassword.text.toString(), null)
// Now allow the authentication to continue
continuation.setAuthenticationDetails(authDetails)
continuation.continueTask()
}
override fun onSuccess(userSession: CognitoUserSession, newDevice: CognitoDevice?) {
progressLoader.visibility = View.GONE
(activity as? OnboardingActivity)?.proceedAfterLogin()
}
override fun onFailure(exception: Exception) {
progressLoader.visibility = View.GONE
val snackbar = Snackbar.make(view, R.string.ERR_GENERAL, Snackbar.LENGTH_LONG)
snackbar.show()
progressLoader.visibility = View.GONE
}
override fun getMFACode(continuation: MultiFactorAuthenticationContinuation) {
continuation.continueTask()
}
override fun authenticationChallenge(continuation: ChallengeContinuation) {
continuation.continueTask()
}
}
loginButton.setOnClickListener {
val userPool = CognitoUserPool(context, AWSMobileClient.getInstance().configuration)
val user = userPool.getUser(inputUsername.text.toString())
progressLoader.visibility = View.VISIBLE
user.getSessionInBackground(authenticationHandler)
}
Api client:
val factory = ApiClientFactory().credentialsProvider(AWSMobileClient.getInstance().credentialsProvider)
val = factory.build(MyClient::class.java)
try {
val request = GetChallengesRequest("", nextPageKey)
val response = client.getRunningChallenges(request)
} catch (t: Throwable) {
// This catch is allways called with the error
}
The config is loaded using the awsconfiguration.json which is stored in the raw resource folder.
When setting a breakpoint in AWS4Signer sign method I can see the sign method is called with AnonymousAWSCredentials but I really can't figure out why, as I call the method after logging in.
I hope someone can help me resolve this weird issue, it's been bugging me for days!

Categories

Resources