Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ sonarqube {

allprojects {
repositories {
jcenter()
google()
mavenCentral()
google()
jcenter()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we not be removing Jcenter? Do we need it for anything ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are couple of libraries we haven't updated to versions shipped in MavenCentral. I'm reluctant to make those changes here but we can schedule them @Peter-John-paystack

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Peter-John-paystack we're moving away from GSON because the new data access classes are all in Kotlin.
Because of Java's type erasure, GSON cannot resolve the return types for the retrofit service functions and it makes the service calls fail.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool happy with the Jcenter, but why Moshi and not Kotlin serialisable just out of interest?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It caused quite a number of conflicts with the kotlin version that was already in the project.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool cool, lets just make sure that we are going to do regression testing on all functionality if we are making this change please. 😊

}

configurations.all {
Expand All @@ -67,7 +67,7 @@ ext {
versionCode = 23

buildToolsVersion = "29.0.2"
versionName = "3.3.1"
versionName = "3.3.2"
}

Object getEnvOrDefault(String propertyName, Object defaultValue) {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ android.useAndroidX=true
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2560m
GROUP=co.paystack.android
VERSION_NAME=3.3.1
VERSION_NAME=3.3.2
POM_DESCRIPTION=Android SDK for Paystack
POM_URL=https://github.com/PaystackHQ/paystack-android
POM_SCM_URL=https://github.com/PaystackHQ/paystack-android
Expand Down
7 changes: 5 additions & 2 deletions paystack/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'jacoco'
apply plugin: 'kotlin-kapt'

jacoco {
toolVersion = "$jacocoVersion"
Expand Down Expand Up @@ -65,16 +66,18 @@ android {

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'co.paystack.android.design.widget:pinpad:1.0.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines"

implementation "com.squareup.moshi:moshi-kotlin:1.14.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.14.0"

implementation "com.pusher:pusher-java-client:$versions.pusher"

testImplementation "org.robolectric:robolectric:$versions.robolectric"
Expand Down
81 changes: 80 additions & 1 deletion paystack/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,2 +1,81 @@
-keepclassmembers class co.paystack.android.api.model.** { <fields>; }
-keepclassmembers class co.paystack.android.model.AvsState { <fields>; }
-keepclassmembers class co.paystack.android.model.** { <fields>; }


# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
@retrofit2.http.* <methods>;
}


##MOSHI
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

-keepclasseswithmembers class * {
@com.squareup.moshi.* <methods>;
}

-keep @com.squareup.moshi.JsonQualifier interface *

# Enum field names are used by the integrated EnumJsonAdapter.
# Annotate enums with @JsonClass(generateAdapter = false) to use them with Moshi.
-keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
<fields>;
}

# The name of @JsonClass types is used to look up the generated adapter.
-keepnames @com.squareup.moshi.JsonClass class *

# Retain generated target class's synthetic defaults constructor and keep DefaultConstructorMarker's
# name. We will look this up reflectively to invoke the type's constructor.
#
# We can't _just_ keep the defaults constructor because Proguard/R8's spec doesn't allow wildcard
# matching preceding parameters.
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker
-keepclassmembers @com.squareup.moshi.JsonClass @kotlin.Metadata class * {
synthetic <init>(...);
}

# Retain generated JsonAdapters if annotated type is retained.
-if @com.squareup.moshi.JsonClass class *
-keep class <1>JsonAdapter {
<init>(...);
<fields>;
}
-if @com.squareup.moshi.JsonClass class **$*
-keep class <1>_<2>JsonAdapter {
<init>(...);
<fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*
-keep class <1>_<2>_<3>JsonAdapter {
<init>(...);
<fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*$*
-keep class <1>_<2>_<3>_<4>JsonAdapter {
<init>(...);
<fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*$*$*
-keep class <1>_<2>_<3>_<4>_<5>JsonAdapter {
<init>(...);
<fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*$*$*$*
-keep class <1>_<2>_<3>_<4>_<5>_<6>JsonAdapter {
<init>(...);
<fields>;
}

-keepclassmembers class kotlin.Metadata {
public <methods>;
}
4 changes: 2 additions & 2 deletions paystack/src/main/java/co/paystack/android/Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ public String getReference() {
return reference;
}

void setReference(String reference) {
public void setReference(String reference) {
this.reference = reference;
}

void setId(String id) {
public void setId(String id) {
this.id = id;
}

Expand Down
30 changes: 17 additions & 13 deletions paystack/src/main/java/co/paystack/android/TransactionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -62,9 +63,11 @@ public void onSuccess(@NotNull ChargeParams params, @NotNull ChargeResponse data
}

@Override
public void onError(@NotNull Throwable e) {
public void onError(@NotNull Throwable e, @Nullable String reference) {
Log.e(LOG_TAG, e.getMessage(), e);
notifyProcessingError(e);
Transaction transaction = new Transaction();
transaction.setReference(reference);
notifyProcessingError(e, transaction);
}
};

Expand Down Expand Up @@ -131,6 +134,7 @@ public void onSuccess(TransactionInitResponse data) {
transactionId,
card.getLast4digits(),
deviceId,
data.getReference(),
null
);
processCharge(params);
Expand All @@ -153,18 +157,17 @@ public void onError(@NotNull Throwable exception) {

private void processChargeResponse(ChargeParams chargeParams, ChargeResponse chargeResponse) {
if (chargeResponse == null) {
notifyProcessingError(new ChargeException("Unknown server response"));
notifyProcessingError(new ChargeException("Unknown server response"), chargeParams.getTransaction());
return;
}

Transaction transaction = new Transaction();
transaction.setId(chargeResponse.getTransactionId());
transaction.setReference(chargeResponse.getReference());

String status = chargeResponse.getStatus();
if (status != null) {
if (status.equalsIgnoreCase("1") || status.equalsIgnoreCase("success")) {
setProcessingOff();
Transaction transaction = new Transaction();
transaction.setId(chargeResponse.getTransactionId());
transaction.setReference(chargeResponse.getReference());
transactionCallback.onSuccess(transaction);
return;
}
Expand All @@ -181,12 +184,12 @@ private void processChargeResponse(ChargeParams chargeParams, ChargeResponse cha
}

if (chargeResponse.getAuth() != null && !chargeResponse.getAuth().equalsIgnoreCase("none")) {
authenticateTransaction(chargeParams, chargeResponse, transaction);
authenticateTransaction(chargeParams, chargeResponse, chargeParams.getTransaction());
return;
}

setProcessingOff();
notifyProcessingError(new ChargeException(chargeResponse.getMessage()));
notifyProcessingError(new ChargeException(chargeResponse.getMessage()), chargeParams.getTransaction());
}

private void authenticateTransaction(ChargeParams chargeParams, ChargeResponse chargeResponse, Transaction transaction) {
Expand Down Expand Up @@ -225,7 +228,7 @@ private void validateOtp(String token, ChargeParams chargeParams) {
paystackRepository.validateTransaction(chargeParams, token, cardProcessCallback);
} catch (Exception ce) {
Log.e(LOG_TAG, ce.getMessage(), ce);
notifyProcessingError(ce);
notifyProcessingError(ce, chargeParams.getTransaction());
}
}

Expand All @@ -235,7 +238,7 @@ private void chargeWithAvs(Address address, ChargeParams chargeParams) {
paystackRepository.validateAddress(chargeParams, address, cardProcessCallback);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
notifyProcessingError(e);
notifyProcessingError(e, chargeParams.getTransaction());
}
}

Expand All @@ -251,7 +254,7 @@ public void onTick(long millisUntilFinished) {
}.start();
} catch (Exception ce) {
Log.e(LOG_TAG, ce.getMessage(), ce);
notifyProcessingError(ce);
notifyProcessingError(ce, chargeParams.getTransaction());
}
}

Expand Down Expand Up @@ -460,7 +463,8 @@ protected void onPostExecute(Address address) {
if (address != null) {
chargeWithAvs(address, chargeParams);
} else {
notifyProcessingError(new Exception("No address provided"));

notifyProcessingError(new Exception("No address provided"), chargeParams.getTransaction());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import co.paystack.android.api.request.ChargeParams
interface ChargeApiCallback {
fun onSuccess(params: ChargeParams, response: ChargeResponse)

fun onError(exception: Throwable)
fun onError(exception: Throwable, reference: String?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal class PaystackRepositoryImpl(private val apiService: PaystackApiService
override fun processCardCharge(chargeParams: ChargeParams, callback: ChargeApiCallback) {
makeApiRequest(
onSuccess = { data -> callback.onSuccess(chargeParams, data) },
onError = { throwable -> callback.onError(throwable) },
onError = { throwable -> callback.onError(throwable, chargeParams.reference) },
apiCall = { apiService.chargeCard(chargeParams.toRequestMap()) }
)
}
Expand All @@ -54,15 +54,15 @@ internal class PaystackRepositoryImpl(private val apiService: PaystackApiService
makeApiRequest(
apiCall = { apiService.validateOtp(requestBody) },
onSuccess = { data -> callback.onSuccess(chargeParams, data) },
onError = { throwable -> callback.onError(throwable) },
onError = { throwable -> callback.onError(throwable, chargeParams.reference) },
)
}

override fun requeryTransaction(chargeParams: ChargeParams, callback: ChargeApiCallback) {
makeApiRequest(
apiCall = { apiService.requeryTransaction(chargeParams.transactionId) },
onSuccess = { data -> callback.onSuccess(chargeParams, data) },
onError = { throwable -> callback.onError(throwable) },
onError = { throwable -> callback.onError(throwable, chargeParams.reference) },
)
}

Expand All @@ -73,7 +73,7 @@ internal class PaystackRepositoryImpl(private val apiService: PaystackApiService
makeApiRequest(
apiCall = { apiService.validateAddress(requestBody) },
onSuccess = { data -> callback.onSuccess(chargeParams, data) },
onError = { throwable -> callback.onError(throwable) },
onError = { throwable -> callback.onError(throwable, chargeParams.reference) },
)
}

Expand Down
30 changes: 10 additions & 20 deletions paystack/src/main/java/co/paystack/android/api/di/ApiComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,36 @@ import co.paystack.android.api.service.ApiService
import co.paystack.android.api.service.PaystackApiService
import co.paystack.android.api.service.converter.WrappedResponseConverter
import co.paystack.android.api.utils.TLSSocketFactory
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.squareup.moshi.Moshi
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.moshi.MoshiConverterFactory
import java.util.concurrent.TimeUnit

internal fun apiComponent(): ApiComponent = ApiModule

internal interface ApiComponent {
val gson: Gson
val tlsV1point2factory: TLSSocketFactory
val okHttpClient: OkHttpClient
val legacyApiService: ApiService
val paystackApiService: PaystackApiService
val paystackRepository: PaystackRepository
}

internal object ApiModule : ApiComponent {
const val LEGACY_API_URL = "https://standard.paystack.co/"
const val PAYSTACK_API_URL = "https://api.paystack.co/"

override val gson = GsonBuilder()
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create()
private const val PAYSTACK_API_URL = "https://api.paystack.co/"

override val tlsV1point2factory = TLSSocketFactory()

override val okHttpClient = OkHttpClient.Builder()
override val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
// Add headers so we get Android version and Paystack Library version
val builder = original.newBuilder()
.header("User-Agent", "Android_" + Build.VERSION.SDK_INT + "_Paystack_" + BuildConfig.VERSION_NAME)
.header(
"User-Agent",
"Android_" + Build.VERSION.SDK_INT + "_Paystack_" + BuildConfig.VERSION_NAME
)
.header("X-Paystack-Build", BuildConfig.VERSION_CODE.toString())
.header("Accept", "application/json")
.method(original.method(), original.body())
Expand All @@ -54,19 +50,13 @@ internal object ApiModule : ApiComponent {
.writeTimeout(5, TimeUnit.MINUTES)
.build()


override val legacyApiService: ApiService = Retrofit.Builder()
.baseUrl(LEGACY_API_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(ApiService::class.java)
private val moshi = Moshi.Builder().build()

override val paystackApiService: PaystackApiService = Retrofit.Builder()
.baseUrl(PAYSTACK_API_URL)
.client(okHttpClient)
.addConverterFactory(WrappedResponseConverter.Factory())
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(MoshiConverterFactory.create(moshi).asLenient())
.build()
.create(PaystackApiService::class.java)

Expand Down

This file was deleted.

Loading