diff --git a/build.gradle b/build.gradle index f5d9aa4..e04e25b 100644 --- a/build.gradle +++ b/build.gradle @@ -43,9 +43,9 @@ sonarqube { allprojects { repositories { - jcenter() - google() mavenCentral() + google() + jcenter() } configurations.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) { diff --git a/gradle.properties b/gradle.properties index 11599ff..8677e85 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/paystack/build.gradle b/paystack/build.gradle index a84717f..ab17091 100644 --- a/paystack/build.gradle +++ b/paystack/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'jacoco' +apply plugin: 'kotlin-kapt' jacoco { toolVersion = "$jacocoVersion" @@ -65,9 +66,8 @@ 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' @@ -75,6 +75,9 @@ dependencies { 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" diff --git a/paystack/proguard-rules.pro b/paystack/proguard-rules.pro index 77e0b4b..76fa3a7 100644 --- a/paystack/proguard-rules.pro +++ b/paystack/proguard-rules.pro @@ -1,2 +1,81 @@ -keepclassmembers class co.paystack.android.api.model.** { ; } --keepclassmembers class co.paystack.android.model.AvsState { ; } +-keepclassmembers class co.paystack.android.model.** { ; } + + +# 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.* ; +} + + +##MOSHI +# JSR 305 annotations are for embedding nullability information. +-dontwarn javax.annotation.** + +-keepclasseswithmembers class * { + @com.squareup.moshi.* ; +} + +-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 { + ; +} + +# 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 (...); +} + +# Retain generated JsonAdapters if annotated type is retained. +-if @com.squareup.moshi.JsonClass class * +-keep class <1>JsonAdapter { + (...); + ; +} +-if @com.squareup.moshi.JsonClass class **$* +-keep class <1>_<2>JsonAdapter { + (...); + ; +} +-if @com.squareup.moshi.JsonClass class **$*$* +-keep class <1>_<2>_<3>JsonAdapter { + (...); + ; +} +-if @com.squareup.moshi.JsonClass class **$*$*$* +-keep class <1>_<2>_<3>_<4>JsonAdapter { + (...); + ; +} +-if @com.squareup.moshi.JsonClass class **$*$*$*$* +-keep class <1>_<2>_<3>_<4>_<5>JsonAdapter { + (...); + ; +} +-if @com.squareup.moshi.JsonClass class **$*$*$*$*$* +-keep class <1>_<2>_<3>_<4>_<5>_<6>JsonAdapter { + (...); + ; +} + +-keepclassmembers class kotlin.Metadata { + public ; +} \ No newline at end of file diff --git a/paystack/src/main/java/co/paystack/android/Transaction.java b/paystack/src/main/java/co/paystack/android/Transaction.java index 4b94bfc..5a50e92 100644 --- a/paystack/src/main/java/co/paystack/android/Transaction.java +++ b/paystack/src/main/java/co/paystack/android/Transaction.java @@ -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; } diff --git a/paystack/src/main/java/co/paystack/android/TransactionManager.java b/paystack/src/main/java/co/paystack/android/TransactionManager.java index 21ff125..801e7bb 100644 --- a/paystack/src/main/java/co/paystack/android/TransactionManager.java +++ b/paystack/src/main/java/co/paystack/android/TransactionManager.java @@ -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; @@ -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); } }; @@ -131,6 +134,7 @@ public void onSuccess(TransactionInitResponse data) { transactionId, card.getLast4digits(), deviceId, + data.getReference(), null ); processCharge(params); @@ -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; } @@ -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) { @@ -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()); } } @@ -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()); } } @@ -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()); } } @@ -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()); } } } diff --git a/paystack/src/main/java/co/paystack/android/api/ChargeApiCallback.kt b/paystack/src/main/java/co/paystack/android/api/ChargeApiCallback.kt index 2bb5c86..d83bcbf 100644 --- a/paystack/src/main/java/co/paystack/android/api/ChargeApiCallback.kt +++ b/paystack/src/main/java/co/paystack/android/api/ChargeApiCallback.kt @@ -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?) } \ No newline at end of file diff --git a/paystack/src/main/java/co/paystack/android/api/PaystackRepositoryImpl.kt b/paystack/src/main/java/co/paystack/android/api/PaystackRepositoryImpl.kt index 60e6603..74995a7 100644 --- a/paystack/src/main/java/co/paystack/android/api/PaystackRepositoryImpl.kt +++ b/paystack/src/main/java/co/paystack/android/api/PaystackRepositoryImpl.kt @@ -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()) } ) } @@ -54,7 +54,7 @@ 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) }, ) } @@ -62,7 +62,7 @@ internal class PaystackRepositoryImpl(private val apiService: PaystackApiService makeApiRequest( apiCall = { apiService.requeryTransaction(chargeParams.transactionId) }, onSuccess = { data -> callback.onSuccess(chargeParams, data) }, - onError = { throwable -> callback.onError(throwable) }, + onError = { throwable -> callback.onError(throwable, chargeParams.reference) }, ) } @@ -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) }, ) } diff --git a/paystack/src/main/java/co/paystack/android/api/di/ApiComponent.kt b/paystack/src/main/java/co/paystack/android/api/di/ApiComponent.kt index 4ceeef9..e0da8d3 100644 --- a/paystack/src/main/java/co/paystack/android/api/di/ApiComponent.kt +++ b/paystack/src/main/java/co/paystack/android/api/di/ApiComponent.kt @@ -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()) @@ -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) diff --git a/paystack/src/main/java/co/paystack/android/api/model/ApiResponse.java b/paystack/src/main/java/co/paystack/android/api/model/ApiResponse.java deleted file mode 100644 index 11ea72a..0000000 --- a/paystack/src/main/java/co/paystack/android/api/model/ApiResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package co.paystack.android.api.model; - -import com.google.gson.annotations.SerializedName; - -/** - * An API response always includes a status and a message - */ -public class ApiResponse extends BaseApiModel { - - @SerializedName("status") - public String status; - - @SerializedName("message") - public String message; - - @SerializedName("errors") - public boolean hasErrors = false; -} diff --git a/paystack/src/main/java/co/paystack/android/api/model/BaseApiModel.java b/paystack/src/main/java/co/paystack/android/api/model/BaseApiModel.java deleted file mode 100644 index a81526e..0000000 --- a/paystack/src/main/java/co/paystack/android/api/model/BaseApiModel.java +++ /dev/null @@ -1,7 +0,0 @@ -package co.paystack.android.api.model; - -/** - * Created by {androidsupport@paystack.co} on 9/17/15. - */ -public abstract class BaseApiModel { -} diff --git a/paystack/src/main/java/co/paystack/android/api/model/ChargeResponse.kt b/paystack/src/main/java/co/paystack/android/api/model/ChargeResponse.kt index 48eba6c..cbe3a05 100644 --- a/paystack/src/main/java/co/paystack/android/api/model/ChargeResponse.kt +++ b/paystack/src/main/java/co/paystack/android/api/model/ChargeResponse.kt @@ -2,35 +2,38 @@ package co.paystack.android.api.model import androidx.annotation.Keep -import com.google.gson.Gson -import com.google.gson.annotations.SerializedName +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Moshi @Keep +@JsonClass(generateAdapter = true) data class ChargeResponse( val status: String?, - @SerializedName("trans") - val transactionId: String, + @Json(name = "trans") + val transactionId: String?, - val reference: String, + val reference: String?, val message: String?, - @SerializedName("otpmessage") + @Json(name = "otpmessage") val otpMessage: String? = null, val auth: String? = null, - @SerializedName("countryCode") + @Json(name = "countryCode") val countryCode: String? = null, - - ) { +) { companion object { fun fromJsonString(jsonString: String?): ChargeResponse { return try { - Gson().fromJson(jsonString, ChargeResponse::class.java) + Moshi.Builder().build() + .adapter(ChargeResponse::class.java) + .fromJson(jsonString) ?: error("Failed to parse charge response") } catch (e: Exception) { ChargeResponse( status = "0", diff --git a/paystack/src/main/java/co/paystack/android/api/model/TransactionApiResponse.java b/paystack/src/main/java/co/paystack/android/api/model/TransactionApiResponse.java deleted file mode 100644 index dc6e1ae..0000000 --- a/paystack/src/main/java/co/paystack/android/api/model/TransactionApiResponse.java +++ /dev/null @@ -1,64 +0,0 @@ -package co.paystack.android.api.model; - -import android.webkit.URLUtil; - -import com.google.gson.Gson; -import com.google.gson.annotations.SerializedName; - -import java.io.Serializable; - -/** - * 3DS would give a redirect url at which we can conclude payment - */ -public class TransactionApiResponse extends ApiResponse implements Serializable { - - @SerializedName("reference") - public String reference; - - @SerializedName("trans") - public String trans; - - @SerializedName("auth") - public String auth; - - @SerializedName("otpmessage") - public String otpmessage; - - @SerializedName("countryCode") - public String avsCountryCode; // Country code for Address Verification on supported international cards - - public static TransactionApiResponse unknownServerResponse() { - TransactionApiResponse t = new TransactionApiResponse(); - t.status = "0"; - t.message = "Unknown server response"; - return t; - } - - public static TransactionApiResponse fromJsonString(String jsonString) { - try { - return new Gson().fromJson(jsonString, TransactionApiResponse.class); - } catch (Exception e) { - TransactionApiResponse t = new TransactionApiResponse(); - t.status = "0"; - t.message = e.getMessage(); - return t; - } - } - - public boolean hasValidReferenceAndTrans() { - return (reference != null) && (trans != null); - } - - public boolean hasValidUrl() { - return otpmessage != null && URLUtil.isValidUrl(otpmessage); - } - - public boolean hasValidOtpMessage() { - return otpmessage != null; - } - - public boolean hasValidAuth() { - return auth != null; - } - -} diff --git a/paystack/src/main/java/co/paystack/android/api/model/TransactionApiResponse.kt b/paystack/src/main/java/co/paystack/android/api/model/TransactionApiResponse.kt new file mode 100644 index 0000000..bd6bf60 --- /dev/null +++ b/paystack/src/main/java/co/paystack/android/api/model/TransactionApiResponse.kt @@ -0,0 +1,47 @@ +package co.paystack.android.api.model + +import android.webkit.URLUtil +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class TransactionApiResponse( + @Json(name = "status") + val status: String? = null, + + @Json(name = "message") + val message: String? = null, + + @JvmField + @Json(name = "reference") + val reference: String? = null, + + @JvmField + @Json(name = "trans") + val trans: String? = null, + + @Json(name = "auth") + val auth: String? = null, + + @Json(name = "otpmessage") + val otpmessage: String? = null, + + @Json(name = "countryCode") + val avsCountryCode: String? = null, +) { + fun hasValidReferenceAndTrans(): Boolean { + return reference != null && trans != null + } + + fun hasValidUrl(): Boolean { + return otpmessage != null && URLUtil.isValidUrl(otpmessage) + } + + fun hasValidOtpMessage(): Boolean { + return otpmessage != null + } + + fun hasValidAuth(): Boolean { + return auth != null + } +} \ No newline at end of file diff --git a/paystack/src/main/java/co/paystack/android/api/model/TransactionInitResponse.kt b/paystack/src/main/java/co/paystack/android/api/model/TransactionInitResponse.kt index f55b26e..ccb7806 100644 --- a/paystack/src/main/java/co/paystack/android/api/model/TransactionInitResponse.kt +++ b/paystack/src/main/java/co/paystack/android/api/model/TransactionInitResponse.kt @@ -1,10 +1,13 @@ package co.paystack.android.api.model -import com.google.gson.annotations.SerializedName +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) data class TransactionInitResponse( - val status: String, + @Json(name = "id") + val transactionId: String, - @SerializedName("id") - val transactionId: String + @Json(name = "reference") + val reference: String? ) diff --git a/paystack/src/main/java/co/paystack/android/api/request/BaseRequestBody.java b/paystack/src/main/java/co/paystack/android/api/request/BaseRequestBody.java deleted file mode 100644 index 6d12788..0000000 --- a/paystack/src/main/java/co/paystack/android/api/request/BaseRequestBody.java +++ /dev/null @@ -1,22 +0,0 @@ -package co.paystack.android.api.request; - -import com.google.gson.annotations.SerializedName; - -import java.util.HashMap; - -/** - * A base for all request bodies - */ -abstract class BaseRequestBody { - static final String FIELD_DEVICE = "device"; - @SerializedName(FIELD_DEVICE) - String device; - - public abstract HashMap getParamsHashMap(); - - protected BaseRequestBody(String deviceId) { - this.device = deviceId; - } - - -} diff --git a/paystack/src/main/java/co/paystack/android/api/request/ChargeParams.kt b/paystack/src/main/java/co/paystack/android/api/request/ChargeParams.kt index 4cbae1b..278a579 100644 --- a/paystack/src/main/java/co/paystack/android/api/request/ChargeParams.kt +++ b/paystack/src/main/java/co/paystack/android/api/request/ChargeParams.kt @@ -1,16 +1,19 @@ package co.paystack.android.api.request +import co.paystack.android.Transaction import co.paystack.android.api.utils.pruneNullValues +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) data class ChargeParams( val clientData: String, val transactionId: String, val last4: String, val deviceId: String, - val handle: String? + val reference: String?, + val handle: String? = null ) { fun toRequestMap() = mapOf( - FIELD_CLIENT_DATA to clientData, FIELD_HANDLE to handle, FIELD_TRANS to transactionId, @@ -21,6 +24,13 @@ data class ChargeParams( fun addPin(pin: String) = copy(handle = pin) + fun getTransaction(): Transaction { + val transaction = Transaction() + transaction.setId(transactionId) + transaction.reference = reference + return transaction + } + companion object { const val FIELD_CLIENT_DATA = "clientdata" const val FIELD_HANDLE = "handle" diff --git a/paystack/src/main/java/co/paystack/android/api/request/ChargeRequestBody.java b/paystack/src/main/java/co/paystack/android/api/request/ChargeRequestBody.java deleted file mode 100644 index 8be3bc4..0000000 --- a/paystack/src/main/java/co/paystack/android/api/request/ChargeRequestBody.java +++ /dev/null @@ -1,143 +0,0 @@ -package co.paystack.android.api.request; - -import com.google.gson.annotations.SerializedName; - -import java.util.HashMap; - -import co.paystack.android.PaystackSdk; -import co.paystack.android.model.Charge; -import co.paystack.android.utils.Crypto; -import co.paystack.android.utils.StringUtils; - -/** - * Charge Request Body - */ -public class ChargeRequestBody extends BaseRequestBody { - - private static final String FIELD_CLIENT_DATA = "clientdata"; - private static final String FIELD_LAST4 = "last4"; - private static final String FIELD_ACCESS_CODE = "access_code"; - private static final String FIELD_PUBLIC_KEY = "public_key"; - private static final String FIELD_EMAIL = "email"; - private static final String FIELD_AMOUNT = "amount"; - private static final String FIELD_REFERENCE = "reference"; - private static final String FIELD_SUBACCOUNT = "subaccount"; - private static final String FIELD_TRANSACTION_CHARGE = "transaction_charge"; - private static final String FIELD_BEARER = "bearer"; - private static final String FIELD_HANDLE = "handle"; - private static final String FIELD_METADATA = "metadata"; - private static final String FIELD_CURRENCY = "currency"; - private static final String FIELD_PLAN = "plan"; - - @SerializedName(FIELD_CLIENT_DATA) - private final String clientData; - - @SerializedName(FIELD_LAST4) - private final String last4; - - @SerializedName(FIELD_PUBLIC_KEY) - private final String public_key; - - @SerializedName(FIELD_ACCESS_CODE) - private final String access_code; - - @SerializedName(FIELD_EMAIL) - private final String email; - - @SerializedName(FIELD_AMOUNT) - private final String amount; - - @SerializedName(FIELD_REFERENCE) - private final String reference; - - @SerializedName(FIELD_SUBACCOUNT) - private final String subaccount; - - @SerializedName(FIELD_TRANSACTION_CHARGE) - private final String transaction_charge; - - @SerializedName(FIELD_BEARER) - private final String bearer; - - @SerializedName(FIELD_HANDLE) - private String handle; - - @SerializedName(FIELD_METADATA) - private String metadata; - - @SerializedName(FIELD_CURRENCY) - private String currency; - - @SerializedName(FIELD_PLAN) - private String plan; - private HashMap additionalParameters; - - public ChargeRequestBody(Charge charge, String deviceId) { - super(deviceId); - this.clientData = Crypto.encrypt(StringUtils.concatenateCardFields(charge.getCard())); - this.last4 = charge.getCard().getLast4digits(); - this.public_key = PaystackSdk.getPublicKey(); - this.email = charge.getEmail(); - this.amount = Integer.toString(charge.getAmount()); - this.reference = charge.getReference(); - this.subaccount = charge.getSubaccount(); - this.transaction_charge = charge.getTransactionCharge() > 0 ? Integer.toString(charge.getTransactionCharge()) : null; - this.bearer = charge.getBearer() != null ? charge.getBearer().name() : null; - this.metadata = charge.getMetadata(); - this.plan = charge.getPlan(); - this.currency = charge.getCurrency(); - this.access_code = charge.getAccessCode(); - this.additionalParameters = charge.getAdditionalParameters(); - } - - public ChargeRequestBody addPin(String pin) { - this.handle = Crypto.encrypt(pin); - return this; - } - - @Override - public HashMap getParamsHashMap() { - // set values will override additional params provided - HashMap params = additionalParameters; - params.put(FIELD_PUBLIC_KEY, public_key); - params.put(FIELD_CLIENT_DATA, clientData); - params.put(FIELD_LAST4, last4); - if (access_code != null) { - params.put(FIELD_ACCESS_CODE, access_code); - } - if (email != null) { - params.put(FIELD_EMAIL, email); - } - if (amount != null) { - params.put(FIELD_AMOUNT, amount); - } - if (handle != null) { - params.put(FIELD_HANDLE, handle); - } - if (reference != null) { - params.put(FIELD_REFERENCE, reference); - } - if (subaccount != null) { - params.put(FIELD_SUBACCOUNT, subaccount); - } - if (transaction_charge != null) { - params.put(FIELD_TRANSACTION_CHARGE, transaction_charge); - } - if (bearer != null) { - params.put(FIELD_BEARER, bearer); - } - if (metadata != null) { - params.put(FIELD_METADATA, metadata); - } - if (plan != null) { - params.put(FIELD_PLAN, plan); - } - if (currency != null) { - params.put(FIELD_CURRENCY, currency); - } - if (device != null) { - params.put(FIELD_DEVICE, device); - } - return params; - } -} diff --git a/paystack/src/main/java/co/paystack/android/api/request/TransactionInitRequestBody.kt b/paystack/src/main/java/co/paystack/android/api/request/TransactionInitRequestBody.kt index 9b618a0..ad72d9e 100644 --- a/paystack/src/main/java/co/paystack/android/api/request/TransactionInitRequestBody.kt +++ b/paystack/src/main/java/co/paystack/android/api/request/TransactionInitRequestBody.kt @@ -2,7 +2,9 @@ package co.paystack.android.api.request import co.paystack.android.api.utils.pruneNullValues import co.paystack.android.model.Charge.Bearer +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) data class TransactionInitRequestBody( val publicKey: String, val email: String, diff --git a/paystack/src/main/java/co/paystack/android/api/request/ValidateRequestBody.java b/paystack/src/main/java/co/paystack/android/api/request/ValidateRequestBody.java deleted file mode 100644 index bcfb015..0000000 --- a/paystack/src/main/java/co/paystack/android/api/request/ValidateRequestBody.java +++ /dev/null @@ -1,51 +0,0 @@ -package co.paystack.android.api.request; - -import com.google.gson.annotations.SerializedName; - -import java.io.Serializable; -import java.util.HashMap; - -public class ValidateRequestBody extends BaseRequestBody implements Serializable { - - private static final String FIELD_TRANS = "trans"; - private static final String FIELD_TOKEN = "token"; - - @SerializedName(FIELD_TRANS) - private String trans; - - @SerializedName(FIELD_TOKEN) - private String token; - - public ValidateRequestBody(String deviceId) { - super(deviceId); - } - - private String getTrans() { - return trans; - } - - public ValidateRequestBody setTrans(String trans) { - this.trans = trans; - return this; - } - - private String getToken() { - return token; - } - - public ValidateRequestBody setToken(String token) { - this.token = token; - return this; - } - - @Override - public HashMap getParamsHashMap() { - HashMap params = new HashMap<>(); - params.put(FIELD_TRANS, getTrans()); - params.put(FIELD_TOKEN, getToken()); - if (device != null) { - params.put(FIELD_DEVICE, device); - } - return params; - } -} \ No newline at end of file diff --git a/paystack/src/main/java/co/paystack/android/api/request/ValidateTransactionParams.kt b/paystack/src/main/java/co/paystack/android/api/request/ValidateTransactionParams.kt index da728e2..b82448e 100644 --- a/paystack/src/main/java/co/paystack/android/api/request/ValidateTransactionParams.kt +++ b/paystack/src/main/java/co/paystack/android/api/request/ValidateTransactionParams.kt @@ -1,7 +1,9 @@ package co.paystack.android.api.request import co.paystack.android.api.utils.pruneNullValues +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) data class ValidateTransactionParams( val transactionId: String, val token: String? = null, diff --git a/paystack/src/main/java/co/paystack/android/api/service/PaystackApiFactory.kt b/paystack/src/main/java/co/paystack/android/api/service/PaystackApiFactory.kt index 2973d6f..ddec85d 100644 --- a/paystack/src/main/java/co/paystack/android/api/service/PaystackApiFactory.kt +++ b/paystack/src/main/java/co/paystack/android/api/service/PaystackApiFactory.kt @@ -5,10 +5,10 @@ import co.paystack.android.BuildConfig 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.GsonBuilder +import com.squareup.moshi.Moshi import okhttp3.OkHttpClient import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.moshi.MoshiConverterFactory import java.security.KeyManagementException import java.security.KeyStoreException import java.security.NoSuchAlgorithmException @@ -20,11 +20,12 @@ import java.util.concurrent.TimeUnit internal object PaystackApiFactory { private const val BASE_URL = "https://api.paystack.co/" - @Throws(NoSuchAlgorithmException::class, KeyManagementException::class, KeyStoreException::class) + @Throws( + NoSuchAlgorithmException::class, + KeyManagementException::class, + KeyStoreException::class + ) fun createRetrofitService(): PaystackApiService { - val gson = GsonBuilder() - .setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'") - .create() val tlsV1point2factory = TLSSocketFactory() val okHttpClient = OkHttpClient.Builder() @@ -32,7 +33,10 @@ internal object PaystackApiFactory { 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()) @@ -49,7 +53,7 @@ internal object PaystackApiFactory { .baseUrl(BASE_URL) .client(okHttpClient) .addConverterFactory(WrappedResponseConverter.Factory()) - .addConverterFactory(GsonConverterFactory.create(gson)) + .addConverterFactory(MoshiConverterFactory.create(Moshi.Builder().build())) .build() return retrofit.create(PaystackApiService::class.java) diff --git a/paystack/src/main/java/co/paystack/android/api/service/converter/WrappedResponseConverter.kt b/paystack/src/main/java/co/paystack/android/api/service/converter/WrappedResponseConverter.kt index 44053b1..02980c9 100644 --- a/paystack/src/main/java/co/paystack/android/api/service/converter/WrappedResponseConverter.kt +++ b/paystack/src/main/java/co/paystack/android/api/service/converter/WrappedResponseConverter.kt @@ -1,11 +1,11 @@ package co.paystack.android.api.service.converter +import com.squareup.moshi.JsonClass import okhttp3.ResponseBody import retrofit2.Converter import retrofit2.Retrofit import java.lang.reflect.ParameterizedType import java.lang.reflect.Type -import kotlin.annotation.AnnotationTarget.FUNCTION class WrappedResponseConverter( private val delegate: Converter> @@ -47,10 +47,11 @@ class WrappedResponseConverter( } } - - open class WrappedResponse( - val `data`: T, - val message: String, - val status: Boolean - ) } + +@JsonClass(generateAdapter = true) +open class WrappedResponse( + val `data`: T, + val message: String, + val status: Boolean +) \ No newline at end of file diff --git a/paystack/src/main/java/co/paystack/android/model/AvsState.kt b/paystack/src/main/java/co/paystack/android/model/AvsState.kt index 06e6813..7ae8398 100644 --- a/paystack/src/main/java/co/paystack/android/model/AvsState.kt +++ b/paystack/src/main/java/co/paystack/android/model/AvsState.kt @@ -1,5 +1,8 @@ package co.paystack.android.model +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) data class AvsState( val name: String, val slug: String,