Skip to content

Commit 7de7304

Browse files
Merge pull request #4 from BitcoinErrorLog/paykit-integration-phase6
Phase 2: Paykit Android Integration - Data Models
2 parents 46b9e2a + 4d1f7a8 commit 7de7304

File tree

9 files changed

+257
-0
lines changed

9 files changed

+257
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package to.bitkit.paykit.models
2+
3+
import kotlinx.serialization.Serializable
4+
import java.util.*
5+
6+
@Serializable
7+
data class AutoPaySettings(
8+
var isEnabled: Boolean = false,
9+
var globalDailyLimitSats: Long = 100000L,
10+
var currentDailySpentSats: Long = 0L,
11+
var lastResetDate: Long = System.currentTimeMillis(),
12+
var requireConfirmation: Boolean = false,
13+
var notifyOnPayment: Boolean = true
14+
) {
15+
fun resetIfNeeded(): AutoPaySettings {
16+
val calendar = Calendar.getInstance()
17+
val today = calendar.get(Calendar.DAY_OF_YEAR)
18+
calendar.timeInMillis = lastResetDate
19+
val resetDay = calendar.get(Calendar.DAY_OF_YEAR)
20+
21+
return if (today != resetDay) {
22+
copy(
23+
currentDailySpentSats = 0L,
24+
lastResetDate = System.currentTimeMillis()
25+
)
26+
} else {
27+
this
28+
}
29+
}
30+
31+
val remainingDailyLimitSats: Long
32+
get() = maxOf(0L, globalDailyLimitSats - currentDailySpentSats)
33+
34+
val dailyUsagePercent: Double
35+
get() = if (globalDailyLimitSats > 0) {
36+
currentDailySpentSats.toDouble() / globalDailyLimitSats.toDouble() * 100.0
37+
} else 0.0
38+
}
39+
40+
@Serializable
41+
data class PeerSpendingLimit(
42+
val id: String,
43+
var peerPubkey: String,
44+
var peerName: String,
45+
var limitSats: Long,
46+
var spentSats: Long = 0L,
47+
var period: String = "daily",
48+
var lastResetDate: Long = System.currentTimeMillis()
49+
) {
50+
companion object {
51+
fun create(peerPubkey: String, peerName: String, limitSats: Long, period: String = "daily"): PeerSpendingLimit {
52+
return PeerSpendingLimit(
53+
id = peerPubkey,
54+
peerPubkey = peerPubkey,
55+
peerName = peerName,
56+
limitSats = limitSats,
57+
period = period
58+
)
59+
}
60+
}
61+
62+
fun resetIfNeeded(): PeerSpendingLimit {
63+
val calendar = Calendar.getInstance()
64+
val calendarReset = Calendar.getInstance().apply { timeInMillis = lastResetDate }
65+
66+
val shouldReset = when (period.lowercase()) {
67+
"daily" -> calendar.get(Calendar.DAY_OF_YEAR) != calendarReset.get(Calendar.DAY_OF_YEAR)
68+
"weekly" -> calendar.get(Calendar.WEEK_OF_YEAR) != calendarReset.get(Calendar.WEEK_OF_YEAR)
69+
"monthly" -> calendar.get(Calendar.MONTH) != calendarReset.get(Calendar.MONTH)
70+
else -> false
71+
}
72+
73+
return if (shouldReset) {
74+
copy(spentSats = 0L, lastResetDate = System.currentTimeMillis())
75+
} else {
76+
this
77+
}
78+
}
79+
80+
val remainingSats: Long
81+
get() = maxOf(0L, limitSats - spentSats)
82+
83+
val usagePercent: Double
84+
get() = if (limitSats > 0) {
85+
spentSats.toDouble() / limitSats.toDouble() * 100.0
86+
} else 0.0
87+
}
88+
89+
@Serializable
90+
data class AutoPayRule(
91+
val id: String = UUID.randomUUID().toString(),
92+
var name: String,
93+
var isEnabled: Boolean = true,
94+
var maxAmountSats: Long? = null,
95+
var allowedMethods: List<String> = emptyList(),
96+
var allowedPeers: List<String> = emptyList(),
97+
var requireConfirmation: Boolean = false,
98+
val createdAt: Long = System.currentTimeMillis()
99+
) {
100+
fun matches(amount: Long, method: String, peer: String): Boolean {
101+
if (!isEnabled) return false
102+
maxAmountSats?.let { max -> if (amount > max) return false }
103+
if (allowedMethods.isNotEmpty() && !allowedMethods.contains(method)) return false
104+
if (allowedPeers.isNotEmpty() && !allowedPeers.contains(peer)) return false
105+
return true
106+
}
107+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package to.bitkit.paykit.models
2+
3+
import kotlinx.serialization.Serializable
4+
5+
/**
6+
* A payment contact (recipient)
7+
*/
8+
@Serializable
9+
data class Contact(
10+
/** Unique identifier (same as public key) */
11+
val id: String,
12+
/** Public key in z-base32 format */
13+
val publicKeyZ32: String,
14+
/** Display name */
15+
var name: String,
16+
/** Optional notes */
17+
var notes: String? = null,
18+
/** When the contact was added (Unix timestamp) */
19+
val createdAt: Long = System.currentTimeMillis(),
20+
/** Last payment to this contact (Unix timestamp, if any) */
21+
var lastPaymentAt: Long? = null,
22+
/** Total number of payments to this contact */
23+
var paymentCount: Int = 0
24+
) {
25+
companion object {
26+
/**
27+
* Create a new contact from public key and name
28+
*/
29+
fun create(publicKeyZ32: String, name: String, notes: String? = null): Contact {
30+
return Contact(
31+
id = publicKeyZ32,
32+
publicKeyZ32 = publicKeyZ32,
33+
name = name,
34+
notes = notes
35+
)
36+
}
37+
}
38+
39+
/**
40+
* Record a payment to this contact
41+
*/
42+
fun recordPayment(): Contact {
43+
return copy(
44+
lastPaymentAt = System.currentTimeMillis(),
45+
paymentCount = paymentCount + 1
46+
)
47+
}
48+
49+
/**
50+
* Abbreviated public key for display (first and last 8 chars)
51+
*/
52+
val abbreviatedKey: String
53+
get() {
54+
if (publicKeyZ32.length <= 16) return publicKeyZ32
55+
return "${publicKeyZ32.take(8)}...${publicKeyZ32.takeLast(8)}"
56+
}
57+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package to.bitkit.paykit.models
2+
3+
// TODO: Implement PaymentRequest model based on demo app
4+
// See: paykit-rs-master/paykit-mobile/android-demo/app/src/main/java/com/paykit/demo/storage/PaymentRequestStorage.kt
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package to.bitkit.paykit.models
2+
3+
// TODO: Implement PrivateEndpoint model based on demo app
4+
// See: paykit-rs-master/paykit-mobile/android-demo/app/src/main/java/com/paykit/demo/storage/PrivateEndpointStorage.kt
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package to.bitkit.paykit.models
2+
3+
import kotlinx.serialization.Serializable
4+
import java.util.UUID
5+
6+
@Serializable
7+
enum class PaymentStatus {
8+
PENDING, COMPLETED, FAILED, REFUNDED
9+
}
10+
11+
@Serializable
12+
enum class PaymentDirection {
13+
SENT, RECEIVED
14+
}
15+
16+
@Serializable
17+
data class Receipt(
18+
val id: String,
19+
val direction: PaymentDirection,
20+
val counterpartyKey: String,
21+
var counterpartyName: String? = null,
22+
val amountSats: Long,
23+
var status: PaymentStatus = PaymentStatus.PENDING,
24+
val paymentMethod: String,
25+
val createdAt: Long = System.currentTimeMillis(),
26+
var completedAt: Long? = null,
27+
var memo: String? = null,
28+
var txId: String? = null,
29+
var proof: String? = null,
30+
var proofVerified: Boolean = false,
31+
var proofVerifiedAt: Long? = null
32+
) {
33+
companion object {
34+
fun create(
35+
direction: PaymentDirection,
36+
counterpartyKey: String,
37+
counterpartyName: String? = null,
38+
amountSats: Long,
39+
paymentMethod: String,
40+
memo: String? = null
41+
): Receipt {
42+
return Receipt(
43+
id = UUID.randomUUID().toString(),
44+
direction = direction,
45+
counterpartyKey = counterpartyKey,
46+
counterpartyName = counterpartyName,
47+
amountSats = amountSats,
48+
paymentMethod = paymentMethod,
49+
memo = memo
50+
)
51+
}
52+
}
53+
54+
fun complete(txId: String? = null): Receipt {
55+
return copy(
56+
status = PaymentStatus.COMPLETED,
57+
completedAt = System.currentTimeMillis(),
58+
txId = txId
59+
)
60+
}
61+
62+
fun fail(): Receipt {
63+
return copy(status = PaymentStatus.FAILED)
64+
}
65+
66+
fun markProofVerified(): Receipt {
67+
return copy(
68+
proofVerified = true,
69+
proofVerifiedAt = System.currentTimeMillis()
70+
)
71+
}
72+
73+
val abbreviatedCounterparty: String
74+
get() {
75+
if (counterpartyKey.length <= 16) return counterpartyKey
76+
return "${counterpartyKey.take(8)}...${counterpartyKey.takeLast(8)}"
77+
}
78+
79+
val displayName: String
80+
get() = counterpartyName ?: abbreviatedCounterparty
81+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package to.bitkit.paykit.models
2+
3+
// TODO: Implement Subscription model based on demo app
4+
// See: paykit-rs-master/paykit-mobile/android-demo/app/src/main/java/com/paykit/demo/storage/SubscriptionStorage.kt

app/src/main/java/to/bitkit/paykit/storage/.gitkeep

Whitespace-only changes.

app/src/main/java/to/bitkit/paykit/viewmodels/.gitkeep

Whitespace-only changes.

app/src/main/java/to/bitkit/ui/paykit/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)