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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# NEXT RELEASE | WIP
## Breaking Changes
- Our internal implementation no longer depends on `kotlinx-datetime`, and now uses Java 8 time classes,
namely `java.time.Instant`.
As a result, if your app or library supports Android versions below API 26, you must enable
**[coreLibraryDesugaring](https://developer.android.com/studio/write/java8-support-table)** if it isn't already. In most cases, this will be evident at compile time,
as the build will fail with an error similar to the following:

```
Dependency XYZ requires core library desugaring to be enabled for :your-app-module.
```

- Add new action `ON_FILE_PAYOUT`. A payout allows a merchant to send money to a user's Cash App account.

# 2.6.0
## Breaking Changes
- Introduces new button styles and deprecates existing styles. This is also replaces
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ buildscript {
google_truth_version = '1.4.5'
startup_version = '1.2.0'
okhttp_version = '5.3.2'
kotlinx_date_version = '0.6.2'
desugar_jdk_libs_version = '2.1.5'

versions = [
'minSdk': 21,
Expand Down
28 changes: 14 additions & 14 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -505,18 +505,18 @@ public final class app/cash/paykit/core/models/response/ApiErrorResponseJsonAdap
}

public final class app/cash/paykit/core/models/response/AuthFlowTriggers {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/datetime/Instant;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/time/Instant;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Lkotlinx/datetime/Instant;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lapp/cash/paykit/core/models/response/AuthFlowTriggers;
public static synthetic fun copy$default (Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lapp/cash/paykit/core/models/response/AuthFlowTriggers;
public final fun component4 ()Ljava/time/Instant;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/time/Instant;)Lapp/cash/paykit/core/models/response/AuthFlowTriggers;
public static synthetic fun copy$default (Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/time/Instant;ILjava/lang/Object;)Lapp/cash/paykit/core/models/response/AuthFlowTriggers;
public fun equals (Ljava/lang/Object;)Z
public final fun getMobileUrl ()Ljava/lang/String;
public final fun getQrCodeImageUrl ()Ljava/lang/String;
public final fun getQrCodeSvgUrl ()Ljava/lang/String;
public final fun getRefreshesAt ()Lkotlinx/datetime/Instant;
public final fun getRefreshesAt ()Ljava/time/Instant;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand Down Expand Up @@ -553,9 +553,9 @@ public final class app/cash/paykit/core/models/response/CustomerProfileJsonAdapt
}

public final class app/cash/paykit/core/models/response/CustomerResponseData {
public fun <init> (Ljava/util/List;Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Lapp/cash/paykit/core/models/response/Origin;Lapp/cash/paykit/core/models/response/RequesterProfile;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lapp/cash/paykit/core/models/response/CustomerProfile;Ljava/util/List;Lapp/cash/paykit/core/models/pii/PiiString;)V
public fun <init> (Ljava/util/List;Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Lapp/cash/paykit/core/models/response/Origin;Lapp/cash/paykit/core/models/response/RequesterProfile;Ljava/lang/String;Ljava/time/Instant;Ljava/time/Instant;Ljava/time/Instant;Lapp/cash/paykit/core/models/response/CustomerProfile;Ljava/util/List;Lapp/cash/paykit/core/models/pii/PiiString;)V
public final fun component1 ()Ljava/util/List;
public final fun component10 ()Lkotlinx/datetime/Instant;
public final fun component10 ()Ljava/time/Instant;
public final fun component11 ()Lapp/cash/paykit/core/models/response/CustomerProfile;
public final fun component12 ()Ljava/util/List;
public final fun component13 ()Lapp/cash/paykit/core/models/pii/PiiString;
Expand All @@ -565,24 +565,24 @@ public final class app/cash/paykit/core/models/response/CustomerResponseData {
public final fun component5 ()Lapp/cash/paykit/core/models/response/Origin;
public final fun component6 ()Lapp/cash/paykit/core/models/response/RequesterProfile;
public final fun component7 ()Ljava/lang/String;
public final fun component8 ()Lkotlinx/datetime/Instant;
public final fun component9 ()Lkotlinx/datetime/Instant;
public final fun copy (Ljava/util/List;Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Lapp/cash/paykit/core/models/response/Origin;Lapp/cash/paykit/core/models/response/RequesterProfile;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lapp/cash/paykit/core/models/response/CustomerProfile;Ljava/util/List;Lapp/cash/paykit/core/models/pii/PiiString;)Lapp/cash/paykit/core/models/response/CustomerResponseData;
public static synthetic fun copy$default (Lapp/cash/paykit/core/models/response/CustomerResponseData;Ljava/util/List;Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Lapp/cash/paykit/core/models/response/Origin;Lapp/cash/paykit/core/models/response/RequesterProfile;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lapp/cash/paykit/core/models/response/CustomerProfile;Ljava/util/List;Lapp/cash/paykit/core/models/pii/PiiString;ILjava/lang/Object;)Lapp/cash/paykit/core/models/response/CustomerResponseData;
public final fun component8 ()Ljava/time/Instant;
public final fun component9 ()Ljava/time/Instant;
public final fun copy (Ljava/util/List;Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Lapp/cash/paykit/core/models/response/Origin;Lapp/cash/paykit/core/models/response/RequesterProfile;Ljava/lang/String;Ljava/time/Instant;Ljava/time/Instant;Ljava/time/Instant;Lapp/cash/paykit/core/models/response/CustomerProfile;Ljava/util/List;Lapp/cash/paykit/core/models/pii/PiiString;)Lapp/cash/paykit/core/models/response/CustomerResponseData;
public static synthetic fun copy$default (Lapp/cash/paykit/core/models/response/CustomerResponseData;Ljava/util/List;Lapp/cash/paykit/core/models/response/AuthFlowTriggers;Ljava/lang/String;Ljava/lang/String;Lapp/cash/paykit/core/models/response/Origin;Lapp/cash/paykit/core/models/response/RequesterProfile;Ljava/lang/String;Ljava/time/Instant;Ljava/time/Instant;Ljava/time/Instant;Lapp/cash/paykit/core/models/response/CustomerProfile;Ljava/util/List;Lapp/cash/paykit/core/models/pii/PiiString;ILjava/lang/Object;)Lapp/cash/paykit/core/models/response/CustomerResponseData;
public fun equals (Ljava/lang/Object;)Z
public final fun getActions ()Ljava/util/List;
public final fun getAuthFlowTriggers ()Lapp/cash/paykit/core/models/response/AuthFlowTriggers;
public final fun getChannel ()Ljava/lang/String;
public final fun getCreatedAt ()Lkotlinx/datetime/Instant;
public final fun getCreatedAt ()Ljava/time/Instant;
public final fun getCustomerProfile ()Lapp/cash/paykit/core/models/response/CustomerProfile;
public final fun getExpiresAt ()Lkotlinx/datetime/Instant;
public final fun getExpiresAt ()Ljava/time/Instant;
public final fun getGrants ()Ljava/util/List;
public final fun getId ()Ljava/lang/String;
public final fun getOrigin ()Lapp/cash/paykit/core/models/response/Origin;
public final fun getReferenceId ()Lapp/cash/paykit/core/models/pii/PiiString;
public final fun getRequesterProfile ()Lapp/cash/paykit/core/models/response/RequesterProfile;
public final fun getStatus ()Ljava/lang/String;
public final fun getUpdatedAt ()Lkotlinx/datetime/Instant;
public final fun getUpdatedAt ()Ljava/time/Instant;
public fun hashCode ()I
public final fun isAuthTokenExpired ()Z
public fun toString ()Ljava/lang/String;
Expand Down
6 changes: 3 additions & 3 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ android {
}

compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
Expand Down Expand Up @@ -66,15 +67,14 @@ android {

dependencies {

coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugar_jdk_libs_version"

// We want to lock this dependency at a lower than latest version to not force transitive updates onto merchants.
//noinspection GradleDependency
ksp("com.squareup.moshi:moshi-kotlin-codegen:$moshi_version")
//noinspection GradleDependency
implementation("com.squareup.moshi:moshi-kotlin:$moshi_version")

// KotlinX Dates.
implementation "org.jetbrains.kotlinx:kotlinx-datetime:$kotlinx_date_version"

// Provides a lifecycle for the whole application process.
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

Expand Down
70 changes: 0 additions & 70 deletions core/consumer-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,3 @@
-keep enum app.cash.paykit.core.models.sdk.CashAppPayCurrency { *; }
-keep enum app.cash.paykit.core.impl.RequestType { *; }
-keep enum app.cash.paykit.core.utils.ThreadPurpose { *; }


# Rules for Kotlin Serializer - a transitive dependency of KotlinX Datetime.
# Can probably be removed after datetime is updated.

# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

# Don't print notes about potential mistakes or omissions in the configuration for kotlinx-serialization classes
# See also https://github.com/Kotlin/kotlinx.serialization/issues/1900
-dontnote kotlinx.serialization.**

# Serialization core uses `java.lang.ClassValue` for caching inside these specified classes.
# If there is no `java.lang.ClassValue` (for example, in Android), then R8/ProGuard will print a warning.
# However, since in this case they will not be used, we can disable these warnings
-dontwarn kotlinx.serialization.internal.ClassValueReferences

# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, replace classes with those containing named companion objects.
-keepattributes InnerClasses # Needed for `getDeclaredClasses`.

-if @kotlinx.serialization.Serializable class
kotlinx.datetime.Instant$Companion, # <-- List serializable classes with named companions.
kotlinx.datetime.Instant$Companion$serializer
{
static **$* *;
}
-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
static <1>$$serializer INSTANCE;
}

# Keep both serializer and serializable classes to save the attribute InnerClasses
-keepclasseswithmembers, allowshrinking, allowobfuscation, allowaccessmodification class
kotlinx.datetime.Instant$Companion, # <-- List serializable classes with named companions.
kotlinx.datetime.Instant$Companion$serializer
{
*;
}

-dontwarn kotlinx.serialization.KSerializer
-dontwarn kotlinx.serialization.Serializable
-dontwarn kotlinx.serialization.descriptors.PrimitiveKind$STRING
-dontwarn kotlinx.serialization.descriptors.PrimitiveKind
-dontwarn kotlinx.serialization.descriptors.SerialDescriptor
-dontwarn kotlinx.serialization.descriptors.SerialDescriptorsKt
-dontwarn kotlinx.serialization.internal.AbstractPolymorphicSerializer
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ internal class PayKitAnalyticsEventDispatcherImpl(
clientId,
status = customerResponseData?.status,
authMobileUrl = customerResponseData?.authFlowTriggers?.mobileUrl,
updatedAt = customerResponseData?.updatedAt?.toEpochMilliseconds(),
createdAt = customerResponseData?.createdAt?.toEpochMilliseconds(),
updatedAt = customerResponseData?.updatedAt?.toEpochMilli(),
createdAt = customerResponseData?.createdAt?.toEpochMilli(),
originType = customerResponseData?.origin?.type,
originId = customerResponseData?.origin?.id,
requestChannel = CHANNEL_IN_APP,
Expand Down
15 changes: 9 additions & 6 deletions core/src/main/java/app/cash/paykit/core/impl/CashAppPayImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ import app.cash.paykit.core.utils.ThreadPurpose.DEFERRED_REFRESH
import app.cash.paykit.core.utils.ThreadPurpose.REFRESH_AUTH_TOKEN
import app.cash.paykit.core.utils.orElse
import app.cash.paykit.logging.CashAppLogger
import kotlinx.datetime.Clock
import java.time.Instant
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toKotlinDuration
import java.time.Duration as JavaDuration

/**
* @param clientId Client Identifier that should be provided by Cash PayKit integration.
Expand Down Expand Up @@ -480,9 +482,9 @@ internal class CashAppPayImpl(
}

// Stop refreshing if the request has expired.
val currentTime = Clock.System.now()
val currentTime = Instant.now()
val hasExpired =
customerResponseData?.expiresAt?.let { expiresAt -> currentTime > expiresAt } ?: false
customerResponseData?.expiresAt?.let { expiresAt -> currentTime.isAfter(expiresAt) } ?: false
if (hasExpired) {
logger.logError(tag = CAP_TAG, msg = "Customer request has expired. Stopping refresh.")
return@createThread
Expand Down Expand Up @@ -535,11 +537,12 @@ internal class CashAppPayImpl(
return
}

val ttlSeconds = customerResponseData.authFlowTriggers.refreshesAt.minus(
val ttl = JavaDuration.between(
customerResponseData.createdAt,
)
customerResponseData.authFlowTriggers.refreshesAt,
).toKotlinDuration()

val refreshDelay = ttlSeconds.inWholeSeconds.minus(TOKEN_REFRESH_WINDOW.inWholeSeconds)
val refreshDelay = ttl.inWholeSeconds.minus(TOKEN_REFRESH_WINDOW.inWholeSeconds)
logger.logVerbose(
tag = CAP_TAG,
msg = "Scheduling unauthorized customer request refresh in $refreshDelay seconds.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package app.cash.paykit.core.models.response

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.datetime.Instant
import java.time.Instant

@JsonClass(generateAdapter = true)
data class AuthFlowTriggers(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import app.cash.paykit.core.models.common.Action
import app.cash.paykit.core.models.pii.PiiString
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.datetime.Clock.System
import kotlinx.datetime.Instant
import java.time.Instant

const val STATUS_PENDING = "PENDING"
const val STATUS_PROCESSING = "PROCESSING"
Expand Down Expand Up @@ -57,8 +56,8 @@ data class CustomerResponseData(
val referenceId: PiiString?,
) {
fun isAuthTokenExpired(): Boolean {
val now = System.now()
val isExpired = now > (authFlowTriggers?.refreshesAt ?: return false)
val now = Instant.now()
val isExpired = now.isAfter(authFlowTriggers?.refreshesAt ?: return false)
return isExpired
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import app.cash.paykit.core.network.adapters.InstantAdapter
import app.cash.paykit.core.network.adapters.PiiStringClearTextAdapter
import app.cash.paykit.core.network.adapters.PiiStringRedactAdapter
import com.squareup.moshi.Moshi
import kotlinx.datetime.Instant
import java.time.Instant

internal object MoshiProvider {
fun provideDefault(redactPii: Boolean = false): Moshi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonReader.Token.NULL
import com.squareup.moshi.JsonWriter
import kotlinx.datetime.Instant
import kotlinx.datetime.toInstant
import java.time.Instant

internal class InstantAdapter : JsonAdapter<Instant>() {

Expand All @@ -29,7 +28,7 @@ internal class InstantAdapter : JsonAdapter<Instant>() {
return reader.nextNull<Instant>()
}
val timeRFC3339 = reader.nextString()
return timeRFC3339.toInstant()
return Instant.parse(timeRFC3339)
}

override fun toJson(writer: JsonWriter, value: Instant?) {
Expand Down
2 changes: 1 addition & 1 deletion core/src/test/java/app/cash/paykit/core/PiiStringTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import kotlinx.datetime.Instant
import org.junit.Test
import java.time.Instant

class PiiStringTests {

Expand Down
Loading