Can't use trial purchase id `android.test.purchased` - android

I prefer to use android.test.purchased for in-app purchase flow testing. but recently got an error and can't use it, i can't click buy in test box.
This is my code
ProductDetails productDetails = skuDetailsINAPMap.get(productId);
if (productDetails == null) {
return "Product ID invalid";
}
ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
ImmutableList.of(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
);
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.build();
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
Any suggestion or tips would be appreciated.Thanks
image error

Related

Getting a 401 Error with Github API and Apollo Client Yet It previously worked

I had successfully queried a list of Github issues from a flutter repository using Github Graphql API and Apollo and was able to fetch them to my application. Strangely, I woke up this morning with a HTTP 401 Error, I am unable to understand where the error is coming from and how to catch and correct it.
2021-03-29 07:51:53.590 2532-3799/smartherd.githubissuetracker E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
Process: smartherd.githubissuetracker, PID: 2532
com.apollographql.apollo.exception.ApolloHttpException: HTTP 401
at com.apollographql.apollo.internal.interceptor.ApolloParseInterceptor.parse(ApolloParseInterceptor.java:108)
at com.apollographql.apollo.internal.interceptor.ApolloParseInterceptor$1.onResponse(ApolloParseInterceptor.java:53)
at com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor$executeHttpCall$1.onResponse(ApolloServerInterceptor.kt:110)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:203)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
The Request
val token = "MyToKenHere"
val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain: Interceptor.Chain ->
val original: Request = chain.request()
val builder: Request.Builder =
original.newBuilder().method(original.method(), original.body())
builder.header("Authorization", "bearer $token")
chain.proceed(builder.build())
}
.build()
val apolloClient: ApolloClient = ApolloClient.builder()
.serverUrl("https://api.github.com/graphql")
.okHttpClient(okHttpClient)
.build()
val results = apolloClient.query(LoadgitQuery()).await()
val theget = results.data?.organization?.repository?.issues?.nodes
arrayList_details.clear()
theget?.forEachIndexed { index, value ->
}
As #Rajasekaran M suggested, I checked and turned out I had to create a new token.
It turns out that sometimes you may (mistakenly) publish sensitive info like GitHub token to some repo. I know, it is a silly mistake but sometimes it happens for all of us.
And from what I noticed, as soon as GitHub sees that you published the token (not sure how it is checked though) it becomes invalid.
val apolloClient= ApolloClient.Builder()
.serverUrl("YourDamin")
.addHttpHeader("Accept","application/json")
.addHttpHeader(AUTHORIZATION, token!!)
.build()
lifecycleScope.launchWhenResumed {
try {
val response = apolloClient.query(UserQuery()).execute()
if (response.hasErrors()) {
} else {
// response==>
}
}catch (e:com.apollographql.apollo3.exception.ApolloHttpException){
try {
if (e.statusCode == 401) {
// true
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
use this code for apollo3

How to get a refresh token on androids GoogleSignIn?

Situation
I am providing a Google login option in my android app using google's recommended GoogleSignInClient and GoogleSignInOptions.
The GoogleSignInOptions are specified like this:
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestServerAuthCode(webClientConfiguration.clientId.clientId)
.requestEmail()
.requestScopes(ScopesProvider.CLOUD_MANAGEMENT_SCOPE, ScopesProvider.FIREBASE_SCOPE)
.build()
These options mean:
we require an authorization code
the users email
a few scopes(not explained more detailed here)
and we are using the default sign in.
After the use logged in with his the callback gives us an GoogleSignInAccount object(see google documentation for more detailed infos).
What my app now needs is an access token as it needs access to certain resources that are included within the permission scopes shown above.
We simply request this token like it is specified in google's oauth2 documentation (using OkHttp3 in this example):
val httpUrl = HttpUrl.parse(webClientConfiguration.tokenUri.toString())!!.newBuilder()
.addQueryParameter("client_id", webClientConfiguration.clientId.clientId)
.addQueryParameter("client_secret", webClientConfiguration.clientId.clientSecret)
.addQueryParameter("code", account!!.serverAuthCode)
.addQueryParameter("grant_type", "authorization_code")
.addQueryParameter("redirect_uri", "http://localhost:1234")
.build()
val tokenRequest = Request.Builder()
.method(
"POST", RequestBody.create(
MediaType.parse("application/x-www-form-urlencoded"),
""
)
)
.url(httpUrl)
.build()
val client = OkHttpClient.Builder().build()
client.newCall(tokenRequest).enqueue(
object : okhttp3.Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
Log.d(LOGTAG, "token exchange request failed", e)
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
Log.d(LOGTAG, "repsonse! ${response.body()!!.string()}")
}
}
)
As specified in the docs, this will return a json object with all the required information:
{
"access_token": "<your-individual-access-token>",
"expires_in": 3599,
"id_token": "<user-signin-id-token>"
}
The problem
This request does not contain the required refreshToken. As the initial request(which contains the refreshtoken) which the GoogleSignIn takes care of is supposed to signal the server that we need an refresh token. As discussed here, we would need to add an access_type: offline parameter to the authentication request. But we can't edit the request, since the GoogleSignIn client takes care of this. How can we recieve an refresh token?
After researching for a long time I found a simple solution which works in my case. When creating the GoogleSignInOptions, we tell the GoogleSignIn that we need an authorization code:
.requestServerAuthCode(webClientConfiguration.clientId.clientId)
it turns out that you literally only have to add true as the second parameter("forceCodeForRefreshToken"):
.requestServerAuthCode(webClientConfiguration.clientId.clientId, true)
Then the client will return a different authorization code, which in return will make the google oauth server give you a refresh token when using the code in an request to get an access token.

OkHttpClient injected with Koin loses Authorization header

I'm adding DI to the existing project, in process I faced problem that header Authorization disappears from request. There is no any exceptions or logs from Retrofit/OkHttp. My dependencies are:
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.okhttp:okhttp:2.7.5'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'org.koin:koin-android:2.1.3'
I create http client using provideClient:
class OkHttpProvider private constructor() {
companion object {
fun provideClient(credentials: UsernamePasswordCredentials? = null, context: Context): OkHttpClient {
val client = OkHttpClient.Builder()
// logs
if (BuildConfig.DEBUG) {
client.addInterceptor(
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
)
}
if (credentials != null) {
val creds = Credentials.basic(credentials.userName, credentials.password)
val headerInterceptor = Interceptor { chain ->
var request = chain.request()
val headers = request
.headers()
.newBuilder()
.add("Authorization", creds)
.build()
request = request.newBuilder().headers(headers).build()
chain.proceed(request)
}
//client.addInterceptor(AccessTokenInterceptor(credentials))
client.addInterceptor(headerInterceptor)
}
client
.callTimeout(60L, TimeUnit.SECONDS)
.connectTimeout(10L, TimeUnit.SECONDS)
.readTimeout(60L, TimeUnit.SECONDS)
.writeTimeout(60L, TimeUnit.SECONDS)
.sslSocketFactory(getSslContext().socketFactory).hostnameVerifier { _, _ -> true }
client.addInterceptor(ChuckInterceptor(context))
return client.build()
}
private fun getSslContext(): SSLContext {
...implementation...
}
}
}
My modules for http client and Retrofit are below:
object HttpClientModule {
val module = module {
single(named(COMMON)) {
OkHttpProvider.provideClient(
get<SharedPreferenceManager>().getUserCredentials(),
androidContext()
)
}
...other versions...
}
const val COMMON = "common"
}
object ApiModule {
val module = module {
single {
RetrofitFactory.getServiceInstance(
ApiService::class.java,
get<SharedPreferenceManager>().getString(LocalDataSource.BUILD_OPTION_API, ""),
get(named(HttpClientModule.COMMON))
)
}
...other apis...
}
}
object RetrofitFactory {
const val GEO_URL = "http://maps.googleapis.com/maps/api/"
fun <T> getServiceInstance(
clazz: Class<T>,
url: String = GEO_URL,
client: OkHttpClient
): T = getRetrofitInstance(url, client).create(clazz)
private fun getRetrofitInstance(
url: String,
client: OkHttpClient
) = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
App starts to work with "admin" user and has some credentials saved in shared preferences, when user starts login with phone and sms and requests are sent with "admin" Authorization header, when user inputs code from sms and his new user credentials are saved in shared preferences. After that app sends two requests and Authorization header isn't presented in them. I saw it in Chuck, I even rechecked it using Charles.
To fix this problem I tried few solutions. Firstly, I changed inject for http client from single to factory, that didn't work. Secondly, I googled the problem, but I didn't mentions of this phenomenon. Thirdly, I wrote AccessTokenInterceptor according to this article and also cover everything with logs. I noticed that interceptor works fine in normal cases, but when Authorization header is missing method intercept is not called. This might be reason why default headerInterceptor also not working. Fourthly, I upgraded versions of Retrofit and OkHttp, this also didn't helped.
I noticed interesting thing about that bug: if I restart app after Retrofit lost Authorization header, app works fine test user is properly logged with correct token. Any attempts to relog without restarting the app fails. Maybe someone had similar problem or knows what is happening here, any ideas are welcomed.
I finally find solution to this problem. The problem was user credentials was passed to provideClient only once, when it's created. At that moment user was logged as admin, and standard user credentials was empty, so http client for ApiService was created without Authorization header.
To solve this I changed AccessTokenInterceptor form article (HttpClientType is a enum to select which credentials need to use):
class AccessTokenInterceptor(
private val sharedPreferenceManager: SharedPreferenceManager,
private val clientType: OkHttpProvider.HttpClientType
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val credentials = getUserCredentials(clientType)
if (credentials != null) {
val accessToken = Credentials.basic(credentials.userName, credentials.password)
val request = newRequestWithAccessToken(chain.request(), accessToken)
return chain.proceed(request)
} else {
return chain.proceed(chain.request())
}
}
private fun getUserCredentials(clientType: OkHttpProvider.HttpClientType): UsernamePasswordCredentials? {
return when (clientType) {
OkHttpProvider.HttpClientType.COMMON -> sharedPreferenceManager.getUserCredentials()
OkHttpProvider.HttpClientType.ADMIN -> ServiceCredentialsUtils.getCredentials(sharedPreferenceManager)
}
}
private fun newRequestWithAccessToken(#NonNull request: Request, #NonNull accessToken: String): Request {
return if (request.header("Authorization") == null) {
request.newBuilder()
.header("Authorization", accessToken)
.build()
} else {
request
}
}
}
Now each time request is sending, Interceptor gets user's credentials and adds header to request.

How can I capture a Retrofit response error in onResponse?

I'm using Retrofit2 in an Android app. Here's my response code:
override fun onResponse(call: Call<Asset>?, response: Response<Asset>?) {
val errorStart = response?.errorBody()?.string() // I get "ERROR" here
if (response != null && response.isSuccessful) {
// A successful response was returned
completion(response.body(), null)
} else {
// Not success
val errorElse = response?.errorBody()?.string() // I get "" here
val error = Error(errorText)
completion(null,error)
}
}
and here's the code I'm using to test my code:
response = Response.Builder()
.code(403)
.message(responseString)
.request(chain.request())
.protocol(Protocol.HTTP_1_0)
.body(ResponseBody.create(MediaType.parse("application/json"), "ERROR"))
.addHeader("content-type", "application/json")
.build()
When the code run and a response is returned response?.errorBody()?.string() is a string and I can capture it, as I have in this code as val errorStart.
...But...
when I run the IF code/logic and attempt to capture response?.errorBody()?.string() in the else code block it's now blank/gone.
Can someone explain what's going on or what I'm doing wrong? What is the proper way to capture error information in OnResponse?
First of all the errorBody() is of type stream, meaning that it is not read until you call the method. Streams can be read only once, take a look here for a detailed explanation.

OAuth 2.0 on Android

I would like to create a simple app to check the abilities of newly launched Google Photos Library API (https://developers.google.com/photos/library/guides/get-started).
However I am facing an 401 error when trying to get album list. I've enabled photos library API in API Console, and in my app I've requested access to this scope using the following code:
val GOOGLE_PHOTOS_SCOPE = Scope("https://www.googleapis.com/auth/photoslibrary.readonly")
if (!GoogleSignIn.hasPermissions(
GoogleSignIn.getLastSignedInAccount(this),
GOOGLE_PHOTOS_SCOPE)) {
GoogleSignIn.requestPermissions(
this,
1,
GoogleSignIn.getLastSignedInAccount(this),
GOOGLE_PHOTOS_SCOPE)
} else {
Log.d(TAG, "Permission granted")
(application as QuizApp).photosLibraryApi.getAlbumList().enqueue(
object: retrofit2.Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>?, t: Throwable?) {
Log.e(TAG, "FAIL ${t.toString()}", t)
}
override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>?) {
Log.d(TAG, "Success ${response.toString()}")
}
}
)
}
I guess I need to somehow provide my client_id and project_id from credentials.json file, but I have no idea how to do it. Anyone done it before? Any tips what's the best way to do Google's OAuth2.0?
After loggin in with the Google account (and this is async operation, so you need a listener - see Google login docs) you can get an authorisation code. This code you need to exchange for an access token, and this access token is then passed as a parameter to all your Google Photos API calls.
I just used a library like OKHTTP (on Android) to build the api request to get this accesstoken and yes, there you need your client key.
Note: JAVA code, but you can of course do the same in Kotlin with different syntax.
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormEncodingBuilder()
.add("grant_type", "authorization_code")
.add("client_id", CLIENT_AUTH_KEY_WEB)
.add("client_secret", CLIENT_SECRET)
.add("redirect_uri", "")
.add("code", ACTUAL AUTHORISATION CODE AFTER LOGIN)
.build();
final Request request = new Request.Builder()
.url("https://www.googleapis.com/oauth2/v4/token")
.post(requestBody)
.build();
After this (and you need to have the listeners on for success or failure - see OKHTTP docs) then you can use the access token to get access to the API.
Of course it should also work with RetroFit as I see you are using, but I have no experience with that library.

Categories

Resources