Implement donation receipts.

This commit is contained in:
Alex Hart
2022-02-22 13:41:36 -04:00
committed by Greyson Parrelli
parent 63dab3f4b0
commit 7b499f96be
35 changed files with 1286 additions and 11 deletions

View File

@@ -0,0 +1,95 @@
package org.thoughtcrime.securesms.database
import android.content.Context
import android.database.Cursor
import androidx.core.content.contentValuesOf
import org.signal.core.util.money.FiatMoney
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
import org.thoughtcrime.securesms.util.CursorUtil
import org.thoughtcrime.securesms.util.SqlUtil
import java.math.BigDecimal
import java.util.Currency
class DonationReceiptDatabase(context: Context, databaseHelper: SignalDatabase) : Database(context, databaseHelper) {
companion object {
private const val TABLE_NAME = "donation_receipt"
private const val ID = "_id"
private const val TYPE = "receipt_type"
private const val DATE = "receipt_date"
private const val AMOUNT = "amount"
private const val CURRENCY = "currency"
private const val SUBSCRIPTION_LEVEL = "subscription_level"
@JvmField
val CREATE_TABLE = """
CREATE TABLE $TABLE_NAME (
$ID INTEGER PRIMARY KEY AUTOINCREMENT,
$TYPE TEXT NOT NULL,
$DATE INTEGER NOT NULL,
$AMOUNT TEXT NOT NULL,
$CURRENCY TEXT NOT NULL,
$SUBSCRIPTION_LEVEL INTEGER NOT NULL
)
""".trimIndent()
val CREATE_INDEXS = arrayOf(
"CREATE INDEX IF NOT EXISTS donation_receipt_type_index ON $TABLE_NAME ($TYPE)",
"CREATE INDEX IF NOT EXISTS donation_receipt_date_index ON $TABLE_NAME ($DATE)"
)
}
fun addReceipt(record: DonationReceiptRecord) {
require(record.id == -1L)
val values = contentValuesOf(
AMOUNT to record.amount.amount.toString(),
CURRENCY to record.amount.currency.currencyCode,
DATE to record.timestamp,
TYPE to record.type.code,
SUBSCRIPTION_LEVEL to record.subscriptionLevel
)
writableDatabase.insert(TABLE_NAME, null, values)
}
fun getReceipt(id: Long): DonationReceiptRecord? {
readableDatabase.query(TABLE_NAME, null, ID_WHERE, SqlUtil.buildArgs(id), null, null, null).use { cursor ->
return if (cursor.moveToNext()) {
readRecord(cursor)
} else {
null
}
}
}
fun getReceipts(type: DonationReceiptRecord.Type?): List<DonationReceiptRecord> {
val (where, whereArgs) = if (type != null) {
"$TYPE = ?" to SqlUtil.buildArgs(type.code)
} else {
null to null
}
readableDatabase.query(TABLE_NAME, null, where, whereArgs, null, null, "$DATE DESC").use { cursor ->
val results = ArrayList<DonationReceiptRecord>(cursor.count)
while (cursor.moveToNext()) {
results.add(readRecord(cursor))
}
return results
}
}
private fun readRecord(cursor: Cursor): DonationReceiptRecord {
return DonationReceiptRecord(
id = CursorUtil.requireLong(cursor, ID),
type = DonationReceiptRecord.Type.fromCode(CursorUtil.requireString(cursor, TYPE)),
amount = FiatMoney(
BigDecimal(CursorUtil.requireString(cursor, AMOUNT)),
Currency.getInstance(CursorUtil.requireString(cursor, CURRENCY))
),
timestamp = CursorUtil.requireLong(cursor, DATE),
subscriptionLevel = CursorUtil.requireInt(cursor, SUBSCRIPTION_LEVEL)
)
}
}

View File

@@ -71,6 +71,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
val groupCallRingDatabase: GroupCallRingDatabase = GroupCallRingDatabase(context, this)
val reactionDatabase: ReactionDatabase = ReactionDatabase(context, this)
val notificationProfileDatabase: NotificationProfileDatabase = NotificationProfileDatabase(context, this)
val donationReceiptDatabase: DonationReceiptDatabase = DonationReceiptDatabase(context, this)
override fun onOpen(db: net.zetetic.database.sqlcipher.SQLiteDatabase) {
db.enableWriteAheadLogging()
@@ -103,6 +104,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
db.execSQL(AvatarPickerDatabase.CREATE_TABLE)
db.execSQL(GroupCallRingDatabase.CREATE_TABLE)
db.execSQL(ReactionDatabase.CREATE_TABLE)
db.execSQL(DonationReceiptDatabase.CREATE_TABLE)
executeStatements(db, SearchDatabase.CREATE_TABLE)
executeStatements(db, RemappedRecordsDatabase.CREATE_TABLE)
executeStatements(db, MessageSendLogDatabase.CREATE_TABLE)
@@ -123,6 +125,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
executeStatements(db, MessageSendLogDatabase.CREATE_INDEXES)
executeStatements(db, GroupCallRingDatabase.CREATE_INDEXES)
executeStatements(db, NotificationProfileDatabase.CREATE_INDEXES)
executeStatements(db, DonationReceiptDatabase.CREATE_INDEXS)
executeStatements(db, MessageSendLogDatabase.CREATE_TRIGGERS)
executeStatements(db, ReactionDatabase.CREATE_TRIGGERS)
@@ -466,5 +469,10 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
@get:JvmName("notificationProfiles")
val notificationProfiles: NotificationProfileDatabase
get() = instance!!.notificationProfileDatabase
@get:JvmStatic
@get:JvmName("donationReceipts")
val donationReceipts: DonationReceiptDatabase
get() = instance!!.donationReceiptDatabase
}
}

View File

@@ -48,9 +48,6 @@ import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.lang.AssertionError
import java.util.ArrayList
import java.util.HashSet
import java.util.LinkedList
import java.util.Locale
@@ -190,8 +187,9 @@ object SignalDatabaseMigrations {
private const val MESSAGE_RANGES = 128
private const val REACTION_TRIGGER_FIX = 129
private const val PNI_STORES = 130
private const val DONATION_RECEIPTS = 131
const val DATABASE_VERSION = 130
const val DATABASE_VERSION = 131
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -2397,6 +2395,25 @@ object SignalDatabaseMigrations {
db.execSQL("DROP TABLE sessions")
db.execSQL("ALTER TABLE sessions_tmp RENAME TO sessions")
}
if (oldVersion < DONATION_RECEIPTS) {
db.execSQL(
// language=sql
"""
CREATE TABLE donation_receipt (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
receipt_type TEXT NOT NULL,
receipt_date INTEGER NOT NULL,
amount TEXT NOT NULL,
currency TEXT NOT NULL,
subscription_level INTEGER NOT NULL
)
""".trimIndent()
)
db.execSQL("CREATE INDEX IF NOT EXISTS donation_receipt_type_index ON donation_receipt (receipt_type);")
db.execSQL("CREATE INDEX IF NOT EXISTS donation_receipt_date_index ON donation_receipt (receipt_date);")
}
}
@JvmStatic

View File

@@ -0,0 +1,50 @@
package org.thoughtcrime.securesms.database.model
import org.signal.core.util.money.FiatMoney
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
import java.util.Currency
data class DonationReceiptRecord(
val id: Long = -1L,
val amount: FiatMoney,
val timestamp: Long,
val type: Type,
val subscriptionLevel: Int
) {
enum class Type(val code: String) {
RECURRING("recurring"),
BOOST("boost");
companion object {
fun fromCode(code: String): Type {
return values().first { it.code == code }
}
}
}
companion object {
@JvmStatic
fun createForSubscription(subscription: ActiveSubscription.Subscription): DonationReceiptRecord {
val activeCurrency = Currency.getInstance(subscription.currency)
val activeAmount = subscription.amount.movePointLeft(activeCurrency.defaultFractionDigits)
return DonationReceiptRecord(
id = -1L,
amount = FiatMoney(activeAmount, activeCurrency),
timestamp = System.currentTimeMillis(),
subscriptionLevel = subscription.level,
type = Type.RECURRING
)
}
fun createForBoost(amount: FiatMoney): DonationReceiptRecord {
return DonationReceiptRecord(
id = -1L,
amount = amount,
timestamp = System.currentTimeMillis(),
subscriptionLevel = -1,
type = Type.BOOST
)
}
}
}