diff --git a/dependencies.gradle b/dependencies.gradle
index 13c1b886fb..70755d9c85 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -55,6 +55,7 @@ dependencyResolutionManagement {
alias('google-libphonenumber').to('com.googlecode.libphonenumber:libphonenumber:8.12.17')
alias('google-play-services-maps').to('com.google.android.gms:play-services-maps:16.1.0')
alias('google-play-services-auth').to('com.google.android.gms:play-services-auth:16.0.1')
+ alias('google-play-services-wallet').to('com.google.android.gms:play-services-wallet:18.1.3')
alias('google-zxing-android-integration').to('com.google.zxing:android-integration:3.1.0')
alias('google-zxing-core').to('com.google.zxing:core:3.2.1')
alias('google-ez-vcard').to('com.googlecode.ez-vcard:ez-vcard:0.9.11')
diff --git a/donations/app/build.gradle b/donations/app/build.gradle
new file mode 100644
index 0000000000..874fe929b0
--- /dev/null
+++ b/donations/app/build.gradle
@@ -0,0 +1,48 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+ id 'witness'
+}
+
+apply from: 'witness-verifications.gradle'
+
+android {
+ compileSdk COMPILE_SDK
+
+ defaultConfig {
+ applicationId "org.signal.donations.app"
+ versionCode 1
+ versionName "1.0"
+ multiDexEnabled true
+
+ minSdk MINIMUM_SDK
+ targetSdk TARGET_SDK
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JAVA_VERSION
+ targetCompatibility JAVA_VERSION
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencyVerification {
+ configuration = '(debug|release)RuntimeClasspath'
+}
+
+dependencies {
+ implementation libs.androidx.core.ktx
+ implementation libs.androidx.appcompat
+ implementation libs.material.material
+ implementation project(':donations')
+ implementation project(':core-util')
+}
diff --git a/donations/app/src/main/AndroidManifest.xml b/donations/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..51461435c7
--- /dev/null
+++ b/donations/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/app/src/main/java/org/signal/donations/app/MainActivity.java b/donations/app/src/main/java/org/signal/donations/app/MainActivity.java
new file mode 100644
index 0000000000..3734f7dddd
--- /dev/null
+++ b/donations/app/src/main/java/org/signal/donations/app/MainActivity.java
@@ -0,0 +1,92 @@
+package org.signal.donations.app;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.gms.wallet.PaymentData;
+
+import org.signal.core.util.logging.Log;
+import org.signal.core.util.money.FiatMoney;
+import org.signal.donations.GooglePayApi;
+
+import java.math.BigDecimal;
+import java.util.Currency;
+import java.util.Locale;
+
+import io.reactivex.rxjava3.disposables.Disposable;
+
+public class MainActivity extends AppCompatActivity implements GooglePayApi.PaymentRequestCallback {
+
+ private static final String TAG = Log.tag(MainActivity.class);
+
+ private View donateButton;
+
+ private GooglePayApi payApi;
+ private Disposable isReadyToPayDisposable;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ donateButton = findViewById(R.id.donate_with_googlepay);
+ donateButton.setVisibility(View.GONE);
+ donateButton.setOnClickListener(v -> requestPayment());
+
+ payApi = new GooglePayApi(this, TestUtil.INSTANCE);
+
+ isReadyToPayDisposable = payApi.queryIsReadyToPay().subscribe(this::presentGooglePayButton, this::presentException);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ payApi.onActivityResult(requestCode, resultCode, data, 1, this);
+ }
+
+ @Override
+ protected void onDestroy() {
+ isReadyToPayDisposable.dispose();
+ super.onDestroy();
+ }
+
+ private void presentGooglePayButton() {
+ Log.d(TAG, "Pay is available, displaying button");
+ donateButton.setVisibility(View.VISIBLE);
+ }
+
+ private void presentException(@NonNull Throwable throwable) {
+ Log.w(TAG, "Could not display pay button", throwable);
+ Toast.makeText(this, "Could not display pay button", Toast.LENGTH_LONG).show();
+ }
+
+ private void requestPayment() {
+ donateButton.setClickable(false);
+
+ payApi.requestPayment(new FiatMoney(BigDecimal.valueOf(4.00), Currency.getInstance(Locale.getDefault())), "Test Purchase", 1);
+ }
+
+ @Override
+ public void onSuccess(PaymentData paymentData) {
+ Toast.makeText(this, "SUCCESS", Toast.LENGTH_SHORT).show();
+ donateButton.setClickable(true);
+ }
+
+ @Override
+ public void onError() {
+ Toast.makeText(this, "ERROR", Toast.LENGTH_SHORT).show();
+ donateButton.setClickable(true);
+ }
+
+ @Override
+ public void onCancelled() {
+ Toast.makeText(this, "CANCELLED", Toast.LENGTH_SHORT).show();
+ donateButton.setClickable(true);
+ }
+}
diff --git a/donations/app/src/main/java/org/signal/donations/app/TestUtil.kt b/donations/app/src/main/java/org/signal/donations/app/TestUtil.kt
new file mode 100644
index 0000000000..a02a7620b6
--- /dev/null
+++ b/donations/app/src/main/java/org/signal/donations/app/TestUtil.kt
@@ -0,0 +1,22 @@
+package org.signal.donations.app
+
+import org.signal.donations.GooglePayApi
+
+object TestUtil : GooglePayApi.Gateway {
+ override fun getTokenizationSpecificationParameters(): Map {
+ return mapOf(
+ "gateway" to "example",
+ "gatewayMerchantId" to "exampleMerchantId"
+ )
+ }
+
+ override val allowedCardNetworks: List = listOf(
+ "AMEX",
+ "DISCOVER",
+ "INTERAC",
+ "JCB",
+ "MASTERCARD",
+ "VISA"
+ )
+
+}
\ No newline at end of file
diff --git a/donations/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/donations/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..18c0565852
--- /dev/null
+++ b/donations/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/drawable/ic_launcher_background.xml b/donations/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..c71b77f4d2
--- /dev/null
+++ b/donations/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/app/src/main/res/drawable/ic_refresh_20.xml b/donations/app/src/main/res/drawable/ic_refresh_20.xml
new file mode 100644
index 0000000000..915e0bbbca
--- /dev/null
+++ b/donations/app/src/main/res/drawable/ic_refresh_20.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/donations/app/src/main/res/layout/activity_main.xml b/donations/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000000..b07556fbe9
--- /dev/null
+++ b/donations/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/donations/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..9244248264
--- /dev/null
+++ b/donations/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/donations/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..9244248264
--- /dev/null
+++ b/donations/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/mipmap-hdpi/ic_launcher.png b/donations/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..a571e60098
Binary files /dev/null and b/donations/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/donations/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/donations/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..61da551c55
Binary files /dev/null and b/donations/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/donations/app/src/main/res/mipmap-mdpi/ic_launcher.png b/donations/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..c41dd28531
Binary files /dev/null and b/donations/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/donations/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/donations/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..db5080a752
Binary files /dev/null and b/donations/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/donations/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/donations/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..6dba46dab1
Binary files /dev/null and b/donations/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/donations/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/donations/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..da31a871c8
Binary files /dev/null and b/donations/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/donations/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/donations/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..15ac681720
Binary files /dev/null and b/donations/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/donations/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/donations/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..b216f2d313
Binary files /dev/null and b/donations/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/donations/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/donations/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..f25a419744
Binary files /dev/null and b/donations/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/donations/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/donations/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..e96783ccce
Binary files /dev/null and b/donations/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/donations/app/src/main/res/values-night/themes.xml b/donations/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000000..469981c422
--- /dev/null
+++ b/donations/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/values/colors.xml b/donations/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..09837df62f
--- /dev/null
+++ b/donations/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/values/strings.xml b/donations/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..704183733d
--- /dev/null
+++ b/donations/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ DonationsTest
+
\ No newline at end of file
diff --git a/donations/app/src/main/res/values/themes.xml b/donations/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..00c51022b9
--- /dev/null
+++ b/donations/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/donations/app/witness-verifications.gradle b/donations/app/witness-verifications.gradle
new file mode 100644
index 0000000000..2c81c28b24
--- /dev/null
+++ b/donations/app/witness-verifications.gradle
@@ -0,0 +1,168 @@
+// Auto-generated, use ./gradlew calculateChecksums to regenerate
+
+dependencyVerification {
+ verify = [
+
+ ['androidx.activity:activity:1.0.0',
+ 'd1bc9842455c2e534415d88c44df4d52413b478db9093a1ba36324f705f44c3d'],
+
+ ['androidx.annotation:annotation-experimental:1.0.0',
+ 'b219d2b568e7e4ba534e09f8c2fd242343df6ccbdfbbe938846f5d740e6b0b11'],
+
+ ['androidx.annotation:annotation:1.2.0',
+ '9029262bddce116e6d02be499e4afdba21f24c239087b76b3b57d7e98b490a36'],
+
+ ['androidx.appcompat:appcompat-resources:1.2.0',
+ 'c470297c03ff3de1c3d15dacf0be0cae63abc10b52f021dd07ae28daa3100fe5'],
+
+ ['androidx.appcompat:appcompat:1.2.0',
+ '3d2131a55a61a777322e2126e0018011efa6339e53b44153eb651b16020cca70'],
+
+ ['androidx.arch.core:core-common:2.1.0',
+ 'fe1237bf029d063e7f29fe39aeaf73ef74c8b0a3658486fc29d3c54326653889'],
+
+ ['androidx.arch.core:core-runtime:2.0.0',
+ '87e65fc767c712b437649c7cee2431ebb4bed6daef82e501d4125b3ed3f65f8e'],
+
+ ['androidx.cardview:cardview:1.0.0',
+ '1193c04c22a3d6b5946dae9f4e8c59d6adde6a71b6bd5d87fb99d82dda1afec7'],
+
+ ['androidx.collection:collection:1.1.0',
+ '632a0e5407461de774409352940e292a291037724207a787820c77daf7d33b72'],
+
+ ['androidx.constraintlayout:constraintlayout-solver:2.0.1',
+ 'b23732edbb3511d937fea1ffef047b0e6c001b50c1921f0d959fc384d706ec6a'],
+
+ ['androidx.constraintlayout:constraintlayout:2.0.1',
+ 'ec15b5d4a2eff07888bc1499ce2e2c6efe24c0ed60cc57b08c9dc4b6fd3c2189'],
+
+ ['androidx.coordinatorlayout:coordinatorlayout:1.1.0',
+ '44a9e30abf56af1025c52a0af506fee9c4131aa55efda52f9fd9451211c5e8cb'],
+
+ ['androidx.core:core-ktx:1.5.0',
+ '5964cfe7a4882da2a00fb6ca3d3a072d04139208186f7bc4b3cb66022764fc42'],
+
+ ['androidx.core:core:1.5.0',
+ '2b279712795689069cfb63e48b3ab63c32a5649bdda44c482eb8f81ca1a72161'],
+
+ ['androidx.cursoradapter:cursoradapter:1.0.0',
+ 'a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564'],
+
+ ['androidx.customview:customview:1.0.0',
+ '20e5b8f6526a34595a604f56718da81167c0b40a7a94a57daa355663f2594df2'],
+
+ ['androidx.documentfile:documentfile:1.0.0',
+ '865a061ef2fad16522f8433536b8d47208c46ff7c7745197dfa1eeb481869487'],
+
+ ['androidx.drawerlayout:drawerlayout:1.0.0',
+ '9402442cdc5a43cf62fb14f8cf98c63342d4d9d9b805c8033c6cf7e802749ac1'],
+
+ ['androidx.dynamicanimation:dynamicanimation:1.0.0',
+ 'ce005162c229bf308d2d5b12fb6cad0874069cbbeaccee63a8193bd08d40de04'],
+
+ ['androidx.fragment:fragment:1.1.0',
+ 'a14c8b8f2153f128e800fbd266a6beab1c283982a29ec570d2cc05d307d81496'],
+
+ ['androidx.interpolator:interpolator:1.0.0',
+ '33193135a64fe21fa2c35eec6688f1a76e512606c0fc83dc1b689e37add7732a'],
+
+ ['androidx.legacy:legacy-support-core-utils:1.0.0',
+ 'a7edcf01d5b52b3034073027bc4775b78a4764bb6202bb91d61c829add8dd1c7'],
+
+ ['androidx.lifecycle:lifecycle-common:2.1.0',
+ '76db6be533bd730fb361c2feb12a2c26d9952824746847da82601ef81f082643'],
+
+ ['androidx.lifecycle:lifecycle-livedata-core:2.0.0',
+ 'fde334ec7e22744c0f5bfe7caf1a84c9d717327044400577bdf9bd921ec4f7bc'],
+
+ ['androidx.lifecycle:lifecycle-livedata:2.0.0',
+ 'c82609ced8c498f0a701a30fb6771bb7480860daee84d82e0a81ee86edf7ba39'],
+
+ ['androidx.lifecycle:lifecycle-runtime:2.1.0',
+ 'e5173897b965e870651e83d9d5af1742d3f532d58863223a390ce3a194c8312b'],
+
+ ['androidx.lifecycle:lifecycle-viewmodel:2.1.0',
+ 'ba55fb7ac1b2828d5327cda8acf7085d990b2b4c43ef336caa67686249b8523d'],
+
+ ['androidx.loader:loader:1.0.0',
+ '11f735cb3b55c458d470bed9e25254375b518b4b1bad6926783a7026db0f5025'],
+
+ ['androidx.localbroadcastmanager:localbroadcastmanager:1.0.0',
+ 'e71c328ceef5c4a7d76f2d86df1b65d65fe2acf868b1a4efd84a3f34336186d8'],
+
+ ['androidx.multidex:multidex:2.0.0',
+ 'c01700091072e0ff5d8ec2d00eac6b8f96ea18646080425e9ce3c6a7b5f66e33'],
+
+ ['androidx.print:print:1.0.0',
+ '1d5c7f3135a1bba661fc373fd72e11eb0a4adbb3396787826dd8e4190d5d9edd'],
+
+ ['androidx.recyclerview:recyclerview:1.1.0',
+ 'f0d2b5a67d0a91ee1b1c73ef2b636a81f3563925ddd15a1d4e1c41ec28de7a4f'],
+
+ ['androidx.savedstate:savedstate:1.0.0',
+ '2510a5619c37579c9ce1a04574faaf323cd0ffe2fc4e20fa8f8f01e5bb402e83'],
+
+ ['androidx.transition:transition:1.2.0',
+ 'a1e059b3bc0b43a58dec0efecdcaa89c82d2bca552ea5bacf6656c46e853157e'],
+
+ ['androidx.vectordrawable:vectordrawable-animated:1.1.0',
+ '76da2c502371d9c38054df5e2b248d00da87809ed058f3363eae87ce5e2403f8'],
+
+ ['androidx.vectordrawable:vectordrawable:1.1.0',
+ '46fd633ac01b49b7fcabc263bf098c5a8b9e9a69774d234edcca04fb02df8e26'],
+
+ ['androidx.versionedparcelable:versionedparcelable:1.1.1',
+ '57e8d93260d18d5b9007c9eed3c64ad159de90c8609ebfc74a347cbd514535a4'],
+
+ ['androidx.viewpager2:viewpager2:1.0.0',
+ 'e95c0031d4cc247cd48196c6287e58d2cee54d9c79b85afea7c90920330275af'],
+
+ ['androidx.viewpager:viewpager:1.0.0',
+ '147af4e14a1984010d8f155e5e19d781f03c1d70dfed02a8e0d18428b8fc8682'],
+
+ ['com.google.android.gms:play-services-base:17.5.0',
+ '198c9e2115f5ce5f91140cd9b481dc6d64dd634ac2d6c6525567dc5fe00065cb'],
+
+ ['com.google.android.gms:play-services-basement:17.5.0',
+ '362301c0da1c765cbbdcf8ea866b6cb62bc130c86d2aa7cc9e9c18a6e51ea79d'],
+
+ ['com.google.android.gms:play-services-identity:17.0.0',
+ '8987c6c303eaaa9c10c403822cf5ae188ee1ce61c3056eb3be2ca4aaecc80b5f'],
+
+ ['com.google.android.gms:play-services-maps:17.0.0',
+ 'f9e479bc57ff423959c6dd9d08d463c677f440e29d90de795418ea27da6c67fb'],
+
+ ['com.google.android.gms:play-services-tasks:17.2.0',
+ 'a131d126145dfe87de04fa904f9ce91753b2d3273851b7d084666a71255792a8'],
+
+ ['com.google.android.gms:play-services-wallet:18.1.3',
+ 'e19d1f4650f51ce2202c092cbe174058860b6558cf26c8be37a732eff3ae1864'],
+
+ ['com.google.android.material:material:1.3.0',
+ 'cbf1e7d69fc236cdadcbd1ec5f6c0a1a41aca6ad1ef7f8481058956270ab1f0a'],
+
+ ['com.google.protobuf:protobuf-javalite:3.10.0',
+ '215a94dbe100130295906b531bb72a26965c7ac8fcd9a75bf8054a8ac2abf4b4'],
+
+ ['com.squareup.okhttp3:okhttp:3.12.10',
+ '3cb8e21cd3ab10d390448f5a475c960732efab583a09c39f06f866a7ed819ebc'],
+
+ ['com.squareup.okio:okio:1.15.0',
+ '693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2'],
+
+ ['io.reactivex.rxjava3:rxjava:3.0.13',
+ '598abaf71dbc970dd0727e6d5f4f786dc999df5b972cbf261316a32e155b2c69'],
+
+ ['org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32',
+ 'e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145'],
+
+ ['org.jetbrains.kotlin:kotlin-stdlib:1.4.32',
+ '13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba'],
+
+ ['org.jetbrains:annotations:13.0',
+ 'ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478'],
+
+ ['org.reactivestreams:reactive-streams:1.0.3',
+ '1dee0481072d19c929b623e155e14d2f6085dc011529a0a0dbefc84cf571d865'],
+ ]
+}
diff --git a/donations/lib/build.gradle b/donations/lib/build.gradle
new file mode 100644
index 0000000000..a771b27f54
--- /dev/null
+++ b/donations/lib/build.gradle
@@ -0,0 +1,55 @@
+plugins {
+ id 'com.android.library'
+ id 'witness'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+}
+
+apply from: 'witness-verifications.gradle'
+
+android {
+ buildToolsVersion BUILD_TOOL_VERSION
+ compileSdkVersion COMPILE_SDK
+
+ defaultConfig {
+ minSdkVersion MINIMUM_SDK
+ targetSdkVersion TARGET_SDK
+ multiDexEnabled true
+ }
+
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ sourceCompatibility JAVA_VERSION
+ targetCompatibility JAVA_VERSION
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ lintOptions {
+ disable 'InvalidVectorPath'
+ }
+}
+
+dependencyVerification {
+ configuration = '(debug|release)RuntimeClasspath'
+}
+
+dependencies {
+ lintChecks project(':lintchecks')
+
+ implementation project(':core-util')
+
+ coreLibraryDesugaring libs.android.tools.desugar
+
+ implementation libs.androidx.core.ktx
+ implementation libs.androidx.annotation
+ implementation libs.androidx.appcompat
+
+ api libs.google.play.services.wallet
+ api libs.square.okhttp3
+ api libs.rxjava3.rxjava
+
+ kapt libs.androidx.annotation
+}
diff --git a/donations/lib/src/main/AndroidManifest.xml b/donations/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..af79577bd4
--- /dev/null
+++ b/donations/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/donations/lib/src/main/java/org/signal/donations/GooglePayApi.kt b/donations/lib/src/main/java/org/signal/donations/GooglePayApi.kt
new file mode 100644
index 0000000000..d8fbe24ebc
--- /dev/null
+++ b/donations/lib/src/main/java/org/signal/donations/GooglePayApi.kt
@@ -0,0 +1,193 @@
+package org.signal.donations
+
+import android.app.Activity
+import android.content.Intent
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.tasks.Task
+import com.google.android.gms.wallet.AutoResolveHelper
+import com.google.android.gms.wallet.IsReadyToPayRequest
+import com.google.android.gms.wallet.PaymentData
+import com.google.android.gms.wallet.PaymentDataRequest
+import com.google.android.gms.wallet.PaymentsClient
+import com.google.android.gms.wallet.Wallet
+import com.google.android.gms.wallet.WalletConstants
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import org.json.JSONArray
+import org.json.JSONException
+import org.json.JSONObject
+import org.signal.core.util.logging.Log
+import org.signal.core.util.money.FiatMoney
+
+/**
+ * Entrypoint for Google Pay APIs
+ *
+ * @param activity The activity the Pay Client will attach itself to
+ * @param gateway The payment gateway (Such as Stripe)
+ */
+class GooglePayApi(private val activity: Activity, private val gateway: Gateway) {
+
+ private val paymentsClient: PaymentsClient
+
+ init {
+ val walletOptions = Wallet.WalletOptions.Builder()
+ .setEnvironment(PAYMENT_ENVIRONMENT)
+ .build()
+
+ paymentsClient = Wallet.getPaymentsClient(activity, walletOptions)
+ }
+
+ /**
+ * Query the Google Pay API to determine whether or not the device has Google Pay available and ready.
+ *
+ * @return A completable which, when it completes, indicates that Google Pay is available, or when it errors, indicates it is not.
+ */
+ fun queryIsReadyToPay(): Completable = Completable.create { emitter ->
+ try {
+ val request: IsReadyToPayRequest = buildIsReadyToPayRequest()
+ val task: Task = paymentsClient.isReadyToPay(request)
+ task.addOnCompleteListener { completedTask ->
+ if (!emitter.isDisposed) {
+ try {
+ val result: Boolean = completedTask.getResult(ApiException::class.java) ?: false
+ if (result) {
+ emitter.onComplete()
+ } else {
+ emitter.onError(Exception("Google Pay is not available."))
+ }
+ } catch (e: ApiException) {
+ emitter.onError(e)
+ }
+ }
+ }
+ } catch (e: JSONException) {
+ emitter.onError(e)
+ }
+ }.subscribeOn(Schedulers.io())
+
+ /**
+ * Launches the Google Pay sheet via an Activity intent. It is up to the caller to pass
+ * through the activity result to onActivityResult.
+ */
+ fun requestPayment(price: FiatMoney, label: String, requestCode: Int) {
+ val paymentDataRequest = getPaymentDataRequest(price, label)
+ val request = PaymentDataRequest.fromJson(paymentDataRequest.toString())
+ AutoResolveHelper.resolveTask(paymentsClient.loadPaymentData(request), activity, requestCode)
+ }
+
+ /**
+ * Checks the activity result for payment data and fires off the corresponding callback. Does nothing if
+ * the request code is not the expected request code.
+ */
+ fun onActivityResult(
+ requestCode: Int,
+ resultCode: Int,
+ data: Intent?,
+ expectedRequestCode: Int,
+ paymentRequestCallback: PaymentRequestCallback
+ ) {
+ if (requestCode != expectedRequestCode) {
+ return
+ }
+
+ when (resultCode) {
+ Activity.RESULT_OK -> {
+ data?.let { intent ->
+ PaymentData.getFromIntent(intent)?.let { paymentRequestCallback.onSuccess(it) }
+ } ?: paymentRequestCallback.onError()
+ }
+ Activity.RESULT_CANCELED -> paymentRequestCallback.onCancelled()
+ AutoResolveHelper.RESULT_ERROR -> {
+ AutoResolveHelper.getStatusFromIntent(data)?.let {
+ Log.w(TAG, "loadPaymentData failed with error code ${it.statusCode}")
+ paymentRequestCallback.onError()
+ }
+ }
+ }
+ }
+
+ private fun getPaymentDataRequest(price: FiatMoney, label: String): JSONObject {
+ return baseRequest.apply {
+ put("merchantInfo", merchantInfo)
+ put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod()))
+ put("transactionInfo", getTransactionInfo(price, label))
+ put("emailRequired", false)
+ put("shippingAddressRequired", false)
+ }
+ }
+
+ private fun getTransactionInfo(price: FiatMoney, label: String): JSONObject {
+ return JSONObject().apply {
+ put("currencyCode", price.currency.currencyCode)
+ put("countryCode", "US")
+ put("totalPriceStatus", "FINAL")
+ put("totalPrice", price.defaultPrecisionString)
+ put("totalPriceLabel", label)
+ put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE")
+ }
+ }
+
+ private fun buildIsReadyToPayRequest(): IsReadyToPayRequest {
+ val isReadyToPayJson: JSONObject = baseRequest.apply {
+ put("allowedPaymentMethods", JSONArray().put(baseCardPaymentMethod()))
+ }
+
+ return IsReadyToPayRequest.fromJson(isReadyToPayJson.toString())
+ }
+
+ private fun gatewayTokenizationSpecification(): JSONObject {
+ return JSONObject().apply {
+ put("type", "PAYMENT_GATEWAY")
+ put("parameters", JSONObject(gateway.getTokenizationSpecificationParameters()))
+ }
+ }
+
+ private fun baseCardPaymentMethod(): JSONObject {
+ return JSONObject().apply {
+ val parameters = JSONObject().apply {
+ put("allowedAuthMethods", allowedCardAuthMethods)
+ put("allowedCardNetworks", JSONArray(gateway.allowedCardNetworks))
+ put("billingAddressRequired", false)
+ }
+
+ put("type", "CARD")
+ put("parameters", parameters)
+ }
+ }
+
+ private fun cardPaymentMethod(): JSONObject {
+ val cardPaymentMethod = baseCardPaymentMethod()
+ cardPaymentMethod.put("tokenizationSpecification", gatewayTokenizationSpecification())
+
+ return cardPaymentMethod
+ }
+
+ companion object {
+ private val TAG = Log.tag(GooglePayApi::class.java)
+
+ private const val PAYMENT_ENVIRONMENT: Int = WalletConstants.ENVIRONMENT_TEST
+ private const val MERCHANT_NAME = "Signal Foundation"
+
+ private val merchantInfo: JSONObject =
+ JSONObject().put("merchantName", MERCHANT_NAME)
+
+ private val allowedCardAuthMethods = JSONArray(listOf("CRYPTOGRAM_3DS"))
+
+ private val baseRequest = JSONObject().apply {
+ put("apiVersion", 2)
+ put("apiVersionMinor", 0)
+ }
+ }
+
+ interface Gateway {
+ fun getTokenizationSpecificationParameters(): Map
+ val allowedCardNetworks: List
+ }
+
+ interface PaymentRequestCallback {
+ fun onSuccess(paymentData: PaymentData)
+ fun onError()
+ fun onCancelled()
+ }
+}
\ No newline at end of file
diff --git a/donations/lib/src/main/java/org/signal/donations/GooglePayPaymentSource.kt b/donations/lib/src/main/java/org/signal/donations/GooglePayPaymentSource.kt
new file mode 100644
index 0000000000..2198efc58f
--- /dev/null
+++ b/donations/lib/src/main/java/org/signal/donations/GooglePayPaymentSource.kt
@@ -0,0 +1,12 @@
+package org.signal.donations
+
+import com.google.android.gms.wallet.PaymentData
+import org.json.JSONObject
+
+class GooglePayPaymentSource(private val paymentData: PaymentData) : StripeApi.PaymentSource {
+ override fun parameterize(): JSONObject {
+ val jsonData = JSONObject(paymentData.toJson())
+ val paymentMethodJsonData = jsonData.getJSONObject("paymentMethodData")
+ return paymentMethodJsonData.getJSONObject("tokenizationData")
+ }
+}
\ No newline at end of file
diff --git a/donations/lib/src/main/java/org/signal/donations/StripeApi.kt b/donations/lib/src/main/java/org/signal/donations/StripeApi.kt
new file mode 100644
index 0000000000..4d0b787ff6
--- /dev/null
+++ b/donations/lib/src/main/java/org/signal/donations/StripeApi.kt
@@ -0,0 +1,307 @@
+package org.signal.donations
+
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import okio.ByteString
+import org.json.JSONObject
+import org.signal.core.util.money.FiatMoney
+import java.io.IOException
+import java.math.BigDecimal
+import java.util.Locale
+
+class StripeApi(private val configuration: Configuration, private val paymentIntentFetcher: PaymentIntentFetcher, private val okHttpClient: OkHttpClient) {
+
+ sealed class CreatePaymentIntentResult {
+ data class AmountIsTooSmall(val amount: FiatMoney) : CreatePaymentIntentResult()
+ data class AmountIsTooLarge(val amount: FiatMoney) : CreatePaymentIntentResult()
+ data class CurrencyIsNotSupported(val currencyCode: String) : CreatePaymentIntentResult()
+ data class Success(val paymentIntent: PaymentIntent) : CreatePaymentIntentResult()
+ }
+
+ fun createPaymentIntent(price: FiatMoney, description: String? = null): Single = Single.fromCallable {
+ if (Validation.isAmountTooSmall(price)) {
+ CreatePaymentIntentResult.AmountIsTooSmall(price)
+ } else if (Validation.isAmountTooLarge(price)) {
+ CreatePaymentIntentResult.AmountIsTooLarge(price)
+ } else if (!Validation.supportedCurrencyCodes.contains(price.currency.currencyCode.toUpperCase(Locale.ROOT))) {
+ CreatePaymentIntentResult.CurrencyIsNotSupported(price.currency.currencyCode)
+ } else {
+ CreatePaymentIntentResult.Success(
+ paymentIntentFetcher.fetchPaymentIntent(
+ price, description
+ )
+ )
+ }
+ }.subscribeOn(Schedulers.io())
+
+ fun confirmPaymentIntent(paymentSource: PaymentSource, paymentIntent: PaymentIntent): Completable = Completable.fromAction {
+ val paymentMethodId = createPaymentMethod(paymentSource).use { response ->
+ val body = response.body()
+ if (body != null) {
+ val paymentMethodObject = body.string().replace("\n", "").let { JSONObject(it) }
+ paymentMethodObject.getString("id")
+ } else {
+ throw IOException("Failed to parse payment method response")
+ }
+ }
+
+ val parameters = mapOf(
+ "client_secret" to paymentIntent.clientSecret,
+ "payment_method" to paymentMethodId
+ )
+
+ postForm("payment_intents/${paymentIntent.id}/confirm", parameters)
+ }.subscribeOn(Schedulers.io())
+
+ private fun createPaymentMethod(paymentSource: PaymentSource): Response {
+ val tokenizationData = paymentSource.parameterize()
+ val parameters = mapOf(
+ "card[token]" to JSONObject((tokenizationData.get("token") as String).replace("\n", "")).getString("id"),
+ "type" to "card"
+ )
+
+ return postForm("payment_methods", parameters)
+ }
+
+ private fun postForm(endpoint: String, parameters: Map): Response {
+ val formBodyBuilder = FormBody.Builder()
+ parameters.forEach { (k, v) ->
+ formBodyBuilder.add(k, v)
+ }
+
+ val request = Request.Builder()
+ .url("${configuration.baseUrl}/$endpoint")
+ .addHeader("Authorization", "Basic ${ByteString.encodeUtf8("${configuration.publishableKey}:").base64()}")
+ .post(formBodyBuilder.build())
+ .build()
+
+ val response = okHttpClient.newCall(request).execute()
+
+ if (response.isSuccessful) {
+ return response
+ } else {
+ throw IOException("postForm failure: ${response.code()}")
+ }
+ }
+
+ object Validation {
+ private val MAX_AMOUNT = BigDecimal(99_999_999)
+
+ fun isAmountTooLarge(fiatMoney: FiatMoney): Boolean {
+ return fiatMoney.amount > MAX_AMOUNT
+ }
+
+ fun isAmountTooSmall(fiatMoney: FiatMoney): Boolean {
+ return fiatMoney.amount < BigDecimal(minimumIntegralChargePerCurrencyCode[fiatMoney.currency.currencyCode] ?: 50)
+ }
+
+ private val minimumIntegralChargePerCurrencyCode: Map = mapOf(
+ "USD" to 50,
+ "AED" to 200,
+ "AUD" to 50,
+ "BGN" to 100,
+ "BRL" to 50,
+ "CAD" to 50,
+ "CHF" to 50,
+ "CZK" to 1500,
+ "DKK" to 250,
+ "EUR" to 50,
+ "GBP" to 30,
+ "HKD" to 400,
+ "HUF" to 17500,
+ "INR" to 50,
+ "JPY" to 50,
+ "MXN" to 10,
+ "MYR" to 2,
+ "NOK" to 300,
+ "NZD" to 50,
+ "PLN" to 200,
+ "RON" to 200,
+ "SEK" to 300,
+ "SGD" to 50
+ )
+
+ val supportedCurrencyCodes: List = listOf(
+ "USD",
+ "AED",
+ "AFN",
+ "ALL",
+ "AMD",
+ "ANG",
+ "AOA",
+ "ARS",
+ "AUD",
+ "AWG",
+ "AZN",
+ "BAM",
+ "BBD",
+ "BDT",
+ "BGN",
+ "BIF",
+ "BMD",
+ "BND",
+ "BOB",
+ "BRL",
+ "BSD",
+ "BWP",
+ "BZD",
+ "CAD",
+ "CDF",
+ "CHF",
+ "CLP",
+ "CNY",
+ "COP",
+ "CRC",
+ "CVE",
+ "CZK",
+ "DJF",
+ "DKK",
+ "DOP",
+ "DZD",
+ "EGP",
+ "ETB",
+ "EUR",
+ "FJD",
+ "FKP",
+ "GBP",
+ "GEL",
+ "GIP",
+ "GMD",
+ "GNF",
+ "GTQ",
+ "GYD",
+ "HKD",
+ "HNL",
+ "HRK",
+ "HTG",
+ "HUF",
+ "IDR",
+ "ILS",
+ "INR",
+ "ISK",
+ "JMD",
+ "JPY",
+ "KES",
+ "KGS",
+ "KHR",
+ "KMF",
+ "KRW",
+ "KYD",
+ "KZT",
+ "LAK",
+ "LBP",
+ "LKR",
+ "LRD",
+ "LSL",
+ "MAD",
+ "MDL",
+ "MGA",
+ "MKD",
+ "MMK",
+ "MNT",
+ "MOP",
+ "MRO",
+ "MUR",
+ "MVR",
+ "MWK",
+ "MXN",
+ "MYR",
+ "MZN",
+ "NAD",
+ "NGN",
+ "NIO",
+ "NOK",
+ "NPR",
+ "NZD",
+ "PAB",
+ "PEN",
+ "PGK",
+ "PHP",
+ "PKR",
+ "PLN",
+ "PYG",
+ "QAR",
+ "RON",
+ "RSD",
+ "RUB",
+ "RWF",
+ "SAR",
+ "SBD",
+ "SCR",
+ "SEK",
+ "SGD",
+ "SHP",
+ "SLL",
+ "SOS",
+ "SRD",
+ "STD",
+ "SZL",
+ "THB",
+ "TJS",
+ "TOP",
+ "TRY",
+ "TTD",
+ "TWD",
+ "TZS",
+ "UAH",
+ "UGX",
+ "UYU",
+ "UZS",
+ "VND",
+ "VUV",
+ "WST",
+ "XAF",
+ "XCD",
+ "XOF",
+ "XPF",
+ "YER",
+ "ZAR",
+ "ZMW"
+ )
+ }
+
+ class Gateway(private val configuration: Configuration) : GooglePayApi.Gateway {
+ override fun getTokenizationSpecificationParameters(): Map {
+ return mapOf(
+ "gateway" to "stripe",
+ "stripe:version" to configuration.version,
+ "stripe:publishableKey" to configuration.publishableKey
+ )
+ }
+
+ override val allowedCardNetworks: List = listOf(
+ "AMEX",
+ "DISCOVER",
+ "JCB",
+ "MASTERCARD",
+ "VISA"
+ )
+ }
+
+ data class Configuration(
+ val publishableKey: String,
+ val baseUrl: String = "https://api.stripe.com/v1",
+ val version: String = "2018-10-31"
+ )
+
+ interface PaymentIntentFetcher {
+ fun fetchPaymentIntent(
+ price: FiatMoney,
+ description: String? = null
+ ): PaymentIntent
+ }
+
+ data class PaymentIntent(
+ val id: String,
+ val clientSecret: String
+ )
+
+ interface PaymentSource {
+ fun parameterize(): JSONObject
+ }
+}
\ No newline at end of file
diff --git a/donations/lib/src/main/res/drawable-ar/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-ar/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..079fd572c7
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-ar/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-bg/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-bg/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..c23c990df9
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-bg/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-ca/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-ca/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..b2a8fd40d8
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-ca/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-cs/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-cs/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..7c55839e8e
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-cs/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-da/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-da/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..622d3198de
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-da/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-de/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-de/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..c3d4c5347b
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-de/donate_with_googlepay_button_content.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-el/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-el/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..cd7adc3857
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-el/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-es/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-es/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..1d5461166f
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-es/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-et/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-et/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..367cec333e
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-et/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-eu/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-eu/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..083062e8fd
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-eu/donate_with_googlepay_button_content.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-fi/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-fi/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..c0022bfc21
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-fi/donate_with_googlepay_button_content.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-fr/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-fr/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..2cf102c30a
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-fr/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-hdpi/googlepay_button_background_image.9.png b/donations/lib/src/main/res/drawable-hdpi/googlepay_button_background_image.9.png
new file mode 100755
index 0000000000..7091d810af
Binary files /dev/null and b/donations/lib/src/main/res/drawable-hdpi/googlepay_button_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-hdpi/googlepay_button_no_shadow_background_image.9.png b/donations/lib/src/main/res/drawable-hdpi/googlepay_button_no_shadow_background_image.9.png
new file mode 100755
index 0000000000..4f9bc6ba76
Binary files /dev/null and b/donations/lib/src/main/res/drawable-hdpi/googlepay_button_no_shadow_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-hr/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-hr/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..5934099041
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-hr/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-id/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-id/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..b7f195bc92
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-id/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-it/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-it/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..6ffa94b4dc
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-it/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-ja/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-ja/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..5909abc9c6
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-ja/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-ko/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-ko/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..a54b6d3191
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-ko/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-mdpi/googlepay_button_background_image.9.png b/donations/lib/src/main/res/drawable-mdpi/googlepay_button_background_image.9.png
new file mode 100755
index 0000000000..a60464a360
Binary files /dev/null and b/donations/lib/src/main/res/drawable-mdpi/googlepay_button_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-mdpi/googlepay_button_no_shadow_background_image.9.png b/donations/lib/src/main/res/drawable-mdpi/googlepay_button_no_shadow_background_image.9.png
new file mode 100755
index 0000000000..d0be7ed2d5
Binary files /dev/null and b/donations/lib/src/main/res/drawable-mdpi/googlepay_button_no_shadow_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-ms/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-ms/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..18c0936d5f
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-ms/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-nl/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-nl/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..4a3dc7bfbc
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-nl/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-no/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-no/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..622d3198de
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-no/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-pl/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-pl/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..2916f90b14
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-pl/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-pt/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-pt/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..e910854870
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-pt/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-ru/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-ru/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..bea5a08460
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-ru/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-sk/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-sk/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..78e03f8add
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-sk/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-sl/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-sl/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..b1a8d5a0d2
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-sl/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-sr/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-sr/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..59f17d2936
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-sr/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-sv/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-sv/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..abf8cfe800
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-sv/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-th/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-th/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..666202619e
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-th/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-tr/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-tr/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..3c9dd5e5dd
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-tr/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-uk/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-uk/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..a92ff95972
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-uk/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-v21/googlepay_button_background.xml b/donations/lib/src/main/res/drawable-v21/googlepay_button_background.xml
new file mode 100755
index 0000000000..4873ef2428
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-v21/googlepay_button_background.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-v21/googlepay_button_no_shadow_background.xml b/donations/lib/src/main/res/drawable-v21/googlepay_button_no_shadow_background.xml
new file mode 100755
index 0000000000..d44e9eb62d
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-v21/googlepay_button_no_shadow_background.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable-xhdpi/googlepay_button_background_image.9.png b/donations/lib/src/main/res/drawable-xhdpi/googlepay_button_background_image.9.png
new file mode 100755
index 0000000000..40f767d533
Binary files /dev/null and b/donations/lib/src/main/res/drawable-xhdpi/googlepay_button_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-xhdpi/googlepay_button_no_shadow_background_image.9.png b/donations/lib/src/main/res/drawable-xhdpi/googlepay_button_no_shadow_background_image.9.png
new file mode 100755
index 0000000000..969cc52a94
Binary files /dev/null and b/donations/lib/src/main/res/drawable-xhdpi/googlepay_button_no_shadow_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-xxhdpi/googlepay_button_background_image.9.png b/donations/lib/src/main/res/drawable-xxhdpi/googlepay_button_background_image.9.png
new file mode 100755
index 0000000000..91035e2a41
Binary files /dev/null and b/donations/lib/src/main/res/drawable-xxhdpi/googlepay_button_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-xxhdpi/googlepay_button_no_shadow_background_image.9.png b/donations/lib/src/main/res/drawable-xxhdpi/googlepay_button_no_shadow_background_image.9.png
new file mode 100755
index 0000000000..dda66dfd0d
Binary files /dev/null and b/donations/lib/src/main/res/drawable-xxhdpi/googlepay_button_no_shadow_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-xxxhdpi/googlepay_button_background_image.9.png b/donations/lib/src/main/res/drawable-xxxhdpi/googlepay_button_background_image.9.png
new file mode 100755
index 0000000000..ab905289bc
Binary files /dev/null and b/donations/lib/src/main/res/drawable-xxxhdpi/googlepay_button_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-xxxhdpi/googlepay_button_no_shadow_background_image.9.png b/donations/lib/src/main/res/drawable-xxxhdpi/googlepay_button_no_shadow_background_image.9.png
new file mode 100755
index 0000000000..e67fdaf516
Binary files /dev/null and b/donations/lib/src/main/res/drawable-xxxhdpi/googlepay_button_no_shadow_background_image.9.png differ
diff --git a/donations/lib/src/main/res/drawable-zh/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable-zh/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..06ee8ed6ba
--- /dev/null
+++ b/donations/lib/src/main/res/drawable-zh/donate_with_googlepay_button_content.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable/donate_with_googlepay_button_content.xml b/donations/lib/src/main/res/drawable/donate_with_googlepay_button_content.xml
new file mode 100755
index 0000000000..ee9b8f7428
--- /dev/null
+++ b/donations/lib/src/main/res/drawable/donate_with_googlepay_button_content.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable/googlepay_button_background.xml b/donations/lib/src/main/res/drawable/googlepay_button_background.xml
new file mode 100755
index 0000000000..b81005b06f
--- /dev/null
+++ b/donations/lib/src/main/res/drawable/googlepay_button_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable/googlepay_button_content.xml b/donations/lib/src/main/res/drawable/googlepay_button_content.xml
new file mode 100755
index 0000000000..b2cdb1d52b
--- /dev/null
+++ b/donations/lib/src/main/res/drawable/googlepay_button_content.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable/googlepay_button_no_shadow_background.xml b/donations/lib/src/main/res/drawable/googlepay_button_no_shadow_background.xml
new file mode 100755
index 0000000000..9543c4bcb6
--- /dev/null
+++ b/donations/lib/src/main/res/drawable/googlepay_button_no_shadow_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/donations/lib/src/main/res/drawable/googlepay_button_overlay.xml b/donations/lib/src/main/res/drawable/googlepay_button_overlay.xml
new file mode 100755
index 0000000000..70c7a92271
--- /dev/null
+++ b/donations/lib/src/main/res/drawable/googlepay_button_overlay.xml
@@ -0,0 +1,12 @@
+
+
+ -
+
+
+
+
+
+
+
+
diff --git a/donations/lib/src/main/res/layout/donate_with_googlepay_button.xml b/donations/lib/src/main/res/layout/donate_with_googlepay_button.xml
new file mode 100755
index 0000000000..797bf8fc52
--- /dev/null
+++ b/donations/lib/src/main/res/layout/donate_with_googlepay_button.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/donations/lib/src/main/res/values/googlepay_strings.xml b/donations/lib/src/main/res/values/googlepay_strings.xml
new file mode 100755
index 0000000000..81d01c2b74
--- /dev/null
+++ b/donations/lib/src/main/res/values/googlepay_strings.xml
@@ -0,0 +1,12 @@
+
+
+ Google Pay
+ Buy with Google Pay
+ Donate With Google Pay
+ Pay With Google Pay
+ Subscribe With Google Pay
+ Book With Google Pay
+ Checkout With Google Pay
+ Order With Google Pay
+ View In Google Pay
+
\ No newline at end of file
diff --git a/donations/lib/witness-verifications.gradle b/donations/lib/witness-verifications.gradle
new file mode 100644
index 0000000000..e9e3cc120f
--- /dev/null
+++ b/donations/lib/witness-verifications.gradle
@@ -0,0 +1,123 @@
+// Auto-generated, use ./gradlew calculateChecksums to regenerate
+
+dependencyVerification {
+ verify = [
+
+ ['androidx.activity:activity:1.0.0',
+ 'd1bc9842455c2e534415d88c44df4d52413b478db9093a1ba36324f705f44c3d'],
+
+ ['androidx.annotation:annotation:1.2.0',
+ '9029262bddce116e6d02be499e4afdba21f24c239087b76b3b57d7e98b490a36'],
+
+ ['androidx.appcompat:appcompat-resources:1.2.0',
+ 'c470297c03ff3de1c3d15dacf0be0cae63abc10b52f021dd07ae28daa3100fe5'],
+
+ ['androidx.appcompat:appcompat:1.2.0',
+ '3d2131a55a61a777322e2126e0018011efa6339e53b44153eb651b16020cca70'],
+
+ ['androidx.arch.core:core-common:2.1.0',
+ 'fe1237bf029d063e7f29fe39aeaf73ef74c8b0a3658486fc29d3c54326653889'],
+
+ ['androidx.arch.core:core-runtime:2.0.0',
+ '87e65fc767c712b437649c7cee2431ebb4bed6daef82e501d4125b3ed3f65f8e'],
+
+ ['androidx.collection:collection:1.1.0',
+ '632a0e5407461de774409352940e292a291037724207a787820c77daf7d33b72'],
+
+ ['androidx.core:core-ktx:1.5.0',
+ '5964cfe7a4882da2a00fb6ca3d3a072d04139208186f7bc4b3cb66022764fc42'],
+
+ ['androidx.core:core:1.5.0',
+ '2b279712795689069cfb63e48b3ab63c32a5649bdda44c482eb8f81ca1a72161'],
+
+ ['androidx.cursoradapter:cursoradapter:1.0.0',
+ 'a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564'],
+
+ ['androidx.customview:customview:1.0.0',
+ '20e5b8f6526a34595a604f56718da81167c0b40a7a94a57daa355663f2594df2'],
+
+ ['androidx.drawerlayout:drawerlayout:1.0.0',
+ '9402442cdc5a43cf62fb14f8cf98c63342d4d9d9b805c8033c6cf7e802749ac1'],
+
+ ['androidx.fragment:fragment:1.1.0',
+ 'a14c8b8f2153f128e800fbd266a6beab1c283982a29ec570d2cc05d307d81496'],
+
+ ['androidx.interpolator:interpolator:1.0.0',
+ '33193135a64fe21fa2c35eec6688f1a76e512606c0fc83dc1b689e37add7732a'],
+
+ ['androidx.lifecycle:lifecycle-common:2.1.0',
+ '76db6be533bd730fb361c2feb12a2c26d9952824746847da82601ef81f082643'],
+
+ ['androidx.lifecycle:lifecycle-livedata-core:2.0.0',
+ 'fde334ec7e22744c0f5bfe7caf1a84c9d717327044400577bdf9bd921ec4f7bc'],
+
+ ['androidx.lifecycle:lifecycle-livedata:2.0.0',
+ 'c82609ced8c498f0a701a30fb6771bb7480860daee84d82e0a81ee86edf7ba39'],
+
+ ['androidx.lifecycle:lifecycle-runtime:2.1.0',
+ 'e5173897b965e870651e83d9d5af1742d3f532d58863223a390ce3a194c8312b'],
+
+ ['androidx.lifecycle:lifecycle-viewmodel:2.1.0',
+ 'ba55fb7ac1b2828d5327cda8acf7085d990b2b4c43ef336caa67686249b8523d'],
+
+ ['androidx.loader:loader:1.0.0',
+ '11f735cb3b55c458d470bed9e25254375b518b4b1bad6926783a7026db0f5025'],
+
+ ['androidx.savedstate:savedstate:1.0.0',
+ '2510a5619c37579c9ce1a04574faaf323cd0ffe2fc4e20fa8f8f01e5bb402e83'],
+
+ ['androidx.vectordrawable:vectordrawable-animated:1.1.0',
+ '76da2c502371d9c38054df5e2b248d00da87809ed058f3363eae87ce5e2403f8'],
+
+ ['androidx.vectordrawable:vectordrawable:1.1.0',
+ '46fd633ac01b49b7fcabc263bf098c5a8b9e9a69774d234edcca04fb02df8e26'],
+
+ ['androidx.versionedparcelable:versionedparcelable:1.1.1',
+ '57e8d93260d18d5b9007c9eed3c64ad159de90c8609ebfc74a347cbd514535a4'],
+
+ ['androidx.viewpager:viewpager:1.0.0',
+ '147af4e14a1984010d8f155e5e19d781f03c1d70dfed02a8e0d18428b8fc8682'],
+
+ ['com.google.android.gms:play-services-base:17.5.0',
+ '198c9e2115f5ce5f91140cd9b481dc6d64dd634ac2d6c6525567dc5fe00065cb'],
+
+ ['com.google.android.gms:play-services-basement:17.5.0',
+ '362301c0da1c765cbbdcf8ea866b6cb62bc130c86d2aa7cc9e9c18a6e51ea79d'],
+
+ ['com.google.android.gms:play-services-identity:17.0.0',
+ '8987c6c303eaaa9c10c403822cf5ae188ee1ce61c3056eb3be2ca4aaecc80b5f'],
+
+ ['com.google.android.gms:play-services-maps:17.0.0',
+ 'f9e479bc57ff423959c6dd9d08d463c677f440e29d90de795418ea27da6c67fb'],
+
+ ['com.google.android.gms:play-services-tasks:17.2.0',
+ 'a131d126145dfe87de04fa904f9ce91753b2d3273851b7d084666a71255792a8'],
+
+ ['com.google.android.gms:play-services-wallet:18.1.3',
+ 'e19d1f4650f51ce2202c092cbe174058860b6558cf26c8be37a732eff3ae1864'],
+
+ ['com.google.protobuf:protobuf-javalite:3.10.0',
+ '215a94dbe100130295906b531bb72a26965c7ac8fcd9a75bf8054a8ac2abf4b4'],
+
+ ['com.squareup.okhttp3:okhttp:3.12.10',
+ '3cb8e21cd3ab10d390448f5a475c960732efab583a09c39f06f866a7ed819ebc'],
+
+ ['com.squareup.okio:okio:1.15.0',
+ '693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2'],
+
+ ['io.reactivex.rxjava3:rxjava:3.0.13',
+ '598abaf71dbc970dd0727e6d5f4f786dc999df5b972cbf261316a32e155b2c69'],
+
+ ['org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32',
+ 'e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145'],
+
+ ['org.jetbrains.kotlin:kotlin-stdlib:1.4.32',
+ '13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba'],
+
+ ['org.jetbrains:annotations:13.0',
+ 'ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478'],
+
+ ['org.reactivestreams:reactive-streams:1.0.3',
+ '1dee0481072d19c929b623e155e14d2f6085dc011529a0a0dbefc84cf571d865'],
+ ]
+}
diff --git a/settings.gradle b/settings.gradle
index a707d93496..4c304474f8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,6 +11,8 @@ include ':device-transfer'
include ':device-transfer-app'
include ':image-editor'
include ':image-editor-app'
+include ':donations'
+include ':donations-app'
project(':app').name = 'Signal-Android'
project(':paging').projectDir = file('paging/lib')
@@ -24,6 +26,9 @@ project(':libsignal-service').projectDir = file('libsignal/service')
project(':image-editor').projectDir = file('image-editor/lib')
project(':image-editor-app').projectDir = file('image-editor/app')
+project(':donations').projectDir = file('donations/lib')
+project(':donations-app').projectDir = file('donations/app')
+
rootProject.name='Signal'
apply from: 'dependencies.gradle'