├── .github
└── workflows
│ ├── android.yml
│ └── publish.yml
├── .gitignore
├── README.md
├── build.gradle
├── demo-app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── co
│ │ └── thepeer
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── co
│ │ │ └── thepeer
│ │ │ └── MainActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── co
│ └── thepeer
│ └── ExampleUnitTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── java-demo
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── java_demo
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── java_demo
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── java_demo
│ └── ExampleUnitTest.java
├── scripts
├── publish-module.gradle
└── publish-root.gradle
├── settings.gradle
└── thepeer-android
├── .gitignore
├── build.gradle
├── config
└── ktlint.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── co
│ └── thepeer
│ └── sdk
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
├── java
│ └── co
│ │ └── thepeer
│ │ └── sdk
│ │ ├── Thepeer.kt
│ │ ├── model
│ │ ├── ThepeerEvent.kt
│ │ ├── ThepeerParam.kt
│ │ └── ThepeerResult.kt
│ │ ├── ui
│ │ ├── ThepeerResultContract.kt
│ │ ├── ThepeerResultListener.kt
│ │ └── activity
│ │ │ └── ThepeerSdkActivity.kt
│ │ └── utils
│ │ ├── Environment.kt
│ │ ├── Extensions.kt
│ │ ├── Logger.kt
│ │ ├── ThepeerConstants.kt
│ │ ├── ThepeerCurrency.kt
│ │ ├── Urls.kt
│ │ └── WebInterface.kt
└── res
│ ├── drawable
│ ├── close_icon.xml
│ ├── dialog_background.xml
│ └── warning.xml
│ ├── layout
│ ├── fragment_thepeer.xml
│ ├── retry_view.xml
│ └── sdk_activity.xml
│ └── values
│ ├── attr.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── test
└── java
└── co
└── thepeer
└── sdk
├── MockData.kt
└── WebInterfaceTest.kt
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 |
5 | pull_request:
6 | push:
7 | branches: [ "master" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: set up JDK 11
17 | uses: actions/setup-java@v3
18 | with:
19 | java-version: '11'
20 | distribution: 'temurin'
21 | cache: gradle
22 |
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 |
26 | - name: Check code quality with Ktlint
27 | run: ./gradlew thepeer-android:ktlint --stacktrace
28 |
29 | - name: Build thepeer-android library
30 | run: ./gradlew thepeer-android:build
31 |
32 | - name: Build Kotlin demo
33 | run: ./gradlew demo-app:build
34 |
35 | - name: Build Java demo
36 | run: ./gradlew java-demo:build
37 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | release:
5 | # We'll run this workflow when a new GitHub release is created
6 | types: [released]
7 |
8 | jobs:
9 | publish:
10 | name: Release build and publish
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Check out code
14 | uses: actions/checkout@v2
15 | - name: Set up JDK 11
16 | uses: actions/setup-java@v2
17 | with:
18 | distribution: adopt
19 | java-version: 11
20 |
21 | # Get publish version from tag
22 | - id: get_version
23 | uses: battila7/get-version-action@v2
24 | - name: Get version from tag
25 | run: echo ${{ steps.get_version.outputs.version-without-v }}
26 |
27 |
28 | # Builds the release artifacts of the library
29 | - name: Release build
30 | run: ./gradlew :thepeer-android:assembleRelease
31 |
32 | # Generates other artifacts (javadocJar is optional)
33 | # - name: Source jar and dokka
34 | # run: ./gradlew androidSourcesJar javadocJar
35 |
36 | # Runs upload, and then closes & releases the repository
37 | - name: Publish to MavenCentral
38 | run: ./gradlew publishReleasePublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository
39 | env:
40 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
41 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
42 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
43 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
44 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
45 | SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
46 | PUBLISH_VERSION: ${{ steps.get_version.outputs.version-without-v }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | /.idea/*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Thepeer official Android SDK
2 |
3 | 
4 |
5 | Thepeer Android SDK gives one integration access to all fintech businesses on your Android App
6 |
7 |
8 | 1. Send
9 | 3. Checkout
10 | 4. Direct Charge
11 |
12 | ## Demo
13 |
14 | | Send | Checkout | Direct Charge |
15 | |------------------------ | --------------------------------------|--------------------------------------|
16 | | |  | |
17 |
18 |
19 |
20 | ## Setup Implementation
21 |
22 | #### Step 1: Add Dependency
23 |
24 | To your root build.gradle file add:
25 |
26 | ```
27 | allprojects {
28 | repositories {
29 | mavenCentral()
30 | }
31 | }
32 | ```
33 |
34 | To your app-level build.gradle file add:
35 |
36 | ```groovy
37 | dependencies {
38 | // ...
39 | implementation "co.thepeer:thepeer-android:[version]"
40 | }
41 | ```
42 |
43 | #### Step 2: Add Public Key to Manifest
44 |
45 | ```
46 |
49 | ```
50 |
51 | #### Step 3: Initialization
52 |
53 | KOTLIN
54 |
55 | ```kotlin
56 |
57 | // initialize Thepeer SDK
58 | override fun onCreate(savedInstanceState: Bundle?) {
59 | super.onCreate(savedInstanceState)
60 |
61 |
62 | // initialize Thepeer SDK
63 | val thepeer = Thepeer.Initiate(
64 | activity = this,
65 | userReference = "YOUR _USER_REFERENCE",
66 | resultListener = resultListener).build()
67 | }
68 |
69 | ```
70 |
71 | JAVA
72 |
73 | ```java
74 |
75 | // initialize Thepeer SDK
76 | @Override
77 | protected void onCreate(Bundle savedInstanceState) {
78 | super.onCreate(savedInstanceState);
79 | setContentView(R.layout.activity_main);
80 |
81 | // initialize Thepeer SDK
82 | Thepeer thepeer = new Thepeer.Initiate(
83 | getResources().getString(R.string.user_reference),
84 | activity,
85 | new ThepeerResultListener())
86 |
87 | }
88 |
89 | ```
90 | | Parameter name | Description | Required |
91 | |------------------------ | --------------------------------------|--------------------------------------|
92 | | `userReference` | The user reference returned by Thepeer API when a user has been indexed |`true`|
93 |
94 | ## Configuration
95 | Every request will require this configuration to initiate a transaction.
96 |
97 | ```
98 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf())
99 |
100 | ```
101 | | Parameter name | Description | Required |
102 | |------------------------ | --------------------------------------|--------------------------------------|
103 | | `amount` | The amount you intend to send and must be pass as an integer in kobo |`true`|
104 | | `currency ` | Currency which can be `"NGN"` or `"USD"` |`true`|
105 | | `meta` | This object should contain additional/optional attributes you would like to have on your transaction response |`false`|
106 |
107 | ## Send
108 |
109 | Initiate the send request by calling the below function
110 |
111 | KOTLIN
112 |
113 | ```kotlin
114 |
115 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf())
116 | thepeer.send(config = config)
117 |
118 | ```
119 |
120 | JAVA
121 |
122 | ```java
123 |
124 | thepeer.send(config);
125 |
126 | ```
127 |
128 | ## Checkout
129 |
130 | Initiate the checkout request by calling the below function
131 |
132 | KOTLIN
133 |
134 | ```kotlin
135 |
136 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf())
137 | thepeer.checkout("email@gmail.com", config = config)
138 |
139 | ```
140 |
141 | JAVA
142 |
143 | ```java
144 |
145 | thepeer.checkout("email@gmail.com", config);
146 |
147 | ```
148 |
149 | ## Direct Charge
150 |
151 | Initiate the Direct Charge request by calling the below function
152 |
153 | KOTLIN
154 |
155 | ```kotlin
156 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf())
157 | thepeer.directCharge(config)
158 |
159 | ```
160 |
161 | JAVA
162 |
163 | ```java
164 |
165 | thepeer.directCharge(config);
166 |
167 | ```
168 |
169 | ## Listener
170 |
171 | Once the request is initiated the SDK will wait from response from the service and notify the App
172 | via `ThepeerResultListener`
173 | KOTLIN
174 |
175 | ```Kotlin
176 | private val resultListener = object : ThepeerResultListener {
177 | override fun onSuccess(response: String) {
178 | // Transaction Successful
179 | Log.v(TAG,response)
180 |
181 | }
182 |
183 | override fun onError(error: Throwable) {
184 | // Transaction Error occured
185 | Log.e(TAG, error.message)
186 | }
187 |
188 | override fun onCancelled() {
189 | // Transaction was cancelled
190 | Log.e(TAG, " Cancelled")
191 | }
192 |
193 | }
194 |
195 | ```
196 |
197 | JAVA
198 |
199 | ```java
200 |
201 | new ThepeerResultListener() {
202 |
203 | @Override
204 | public void onSuccess(@NonNull String transaction) {
205 | ((TextView) findViewById(R.id.resultText)).setText(transaction.toString());
206 | }
207 |
208 | @Override
209 | public void onCancelled() {
210 |
211 | }
212 |
213 | @Override
214 | public void onError(@NonNull Throwable error) {
215 |
216 | }
217 |
218 | }
219 |
220 |
221 | ```
222 | ## Response structure
223 | ```JSON
224 | {
225 | "event": "send.success",
226 | "type": "send.success",
227 | "data": {
228 | "id": "744bdf8f-17a6-46ae-bda1-b348c6d22f89",
229 | "amount": 100000,
230 | "channel": "send",
231 | "refund": false,
232 | "checkout": null,
233 | "user": {
234 | "reference": "73f03de5-1043-4ad1-bc2e-aa4d94ebee4f",
235 | "name": "Doreen Okoh",
236 | "identifier": "doreen",
237 | "identifier_type": "username",
238 | "email": "doreen@okoh.co.uk",
239 | "created_at": "2021-04-19T19:50:26.000000Z",
240 | "updated_at": "2022-02-14T22:58:25.000000Z"
241 | },
242 | "charge": 1000,
243 | "currency": "NGN",
244 | "mode": "debit",
245 | "reference": "d34dfaebd727e40a8f436a4b43acbf73",
246 | "remark": "food",
247 | "status": "success",
248 | "type": "peer",
249 | "meta": null,
250 | "peer": {
251 | "business": {
252 | "name": "Cash App",
253 | "logo": "https://palaciodepeer.s3.us-east-2.amazonaws.com/business_logos/UJimBqYOu7KQIM3DwCWOuKjkDbBbVLYRuYRTgxKh.png",
254 | "logo_colour": "#77cc33"
255 | },
256 | "user": {
257 | "name": "Trojan Okoh",
258 | "identifier": "trojan",
259 | "identifier_type": "username"
260 | }
261 | },
262 | "updated_at": "2023-05-25T12:32:03.000000Z",
263 | "created_at": "2023-05-25T12:32:03.000000Z"
264 | }
265 | }
266 | ```
267 | ## Support
268 |
269 | If you're having trouble with Thepeer React or your integration, please reach out to us at . We're more than happy to help you out.
270 |
271 | ## Thepeer API References
272 |
273 | - [Thepeer API Docs](https://docs.thepeer.co)
274 | - [Thepeer Dashboard](https://dashboard.thepeer.co)
275 |
276 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 |
2 | plugins{
3 | id 'com.android.application' version '7.4.0' apply false
4 | id 'com.android.library' version '7.4.0' apply false
5 | id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
6 | id 'io.github.gradle-nexus.publish-plugin' version '1.1.0'
7 | }
8 |
9 |
10 | ext {
11 | kotlin_version = '1.7.21'
12 |
13 | //ktlint
14 | ktlintVersion = '0.46.1'
15 |
16 | // Coroutine
17 | coroutineVersion = '1.5.2'
18 | coroutineTestVersion = '1.5.2'
19 |
20 | // OkHTTP
21 | okHttpVersion = '4.9.2'
22 |
23 | // Moshi
24 | moshiVersion = '1.12.0'
25 | moshiAdaptersVersion = '1.12.0'
26 | moshiKotlinVersion = '1.12.0'
27 |
28 | // Mockk
29 | mockkVersion = '1.12.0'
30 |
31 | // Junit
32 | junitVersion = '4.13.2'
33 |
34 | //Navigation graph
35 | navGraph = '2.4.1'
36 |
37 | //lifecycle
38 | lifecycleVersion = '2.5.0-alpha03'
39 |
40 | // Robolectric
41 | robolectricVersion = '4.7.2'
42 | }
43 |
44 | apply from: "${rootDir}/scripts/publish-root.gradle"
--------------------------------------------------------------------------------
/demo-app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/demo-app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-kapt'
5 | }
6 |
7 |
8 | android {
9 | compileSdkVersion 33
10 |
11 |
12 | defaultConfig {
13 | applicationId "co.thepeer"
14 | minSdkVersion 23
15 | targetSdkVersion 33
16 | versionCode 1
17 | versionName "1.0"
18 |
19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20 | }
21 |
22 | buildFeatures {
23 | viewBinding true
24 | }
25 |
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 | kotlinOptions {
37 | jvmTarget = '1.8'
38 | }
39 | }
40 |
41 | dependencies {
42 |
43 | implementation project(":thepeer-android")
44 | implementation 'androidx.core:core-ktx:1.7.0'
45 | implementation 'androidx.appcompat:appcompat:1.4.1'
46 | implementation 'com.google.android.material:material:1.5.0'
47 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
48 | implementation 'androidx.core:core-ktx:1.10.1'
49 | testImplementation 'junit:junit:4.+'
50 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
51 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
52 | }
--------------------------------------------------------------------------------
/demo-app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/demo-app/src/androidTest/java/co/thepeer/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("co.thepeer", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/demo-app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo-app/src/main/java/co/thepeer/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import co.thepeer.databinding.ActivityMainBinding
6 | import co.thepeer.sdk.Thepeer
7 | import co.thepeer.sdk.model.ThepeerConfig
8 | import co.thepeer.sdk.ui.ThepeerResultListener
9 | import co.thepeer.sdk.utils.ThepeerCurrency
10 | import java.math.BigDecimal
11 |
12 | class MainActivity : AppCompatActivity() {
13 |
14 | private lateinit var binding: ActivityMainBinding
15 |
16 | lateinit var thepeer: Thepeer
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | binding = ActivityMainBinding.inflate(layoutInflater)
20 | setContentView(binding.root)
21 |
22 | // initialize Thepeer SDK
23 | val thepeer = Thepeer.Initiate(
24 | activity = this,
25 | userReference = getString(R.string.user_reference),
26 | resultListener = resultListener,
27 | ).build()
28 |
29 | binding.btnSendMoney.setOnClickListener {
30 | // calling ThePeer SDK
31 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = ThepeerCurrency.NGN)
32 | thepeer.send(config = config)
33 | }
34 | binding.btnCheckout.setOnClickListener {
35 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = ThepeerCurrency.NGN)
36 | thepeer.checkout("email@gmail.com", config = config)
37 | }
38 | binding.btnDirectDebit.setOnClickListener {
39 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = ThepeerCurrency.NGN)
40 | thepeer.directCharge(config)
41 | }
42 | }
43 |
44 | private val resultListener = object : ThepeerResultListener {
45 | override fun onSuccess(response: String) {
46 | binding.resultText.text = response
47 | }
48 |
49 | override fun onError(error: Throwable) {
50 | binding.resultText.text = error.message
51 | }
52 |
53 | override fun onCancelled() {
54 | binding.resultText.text = " Cancelled"
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
26 |
27 |
35 |
36 |
45 |
46 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demo-app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ThepeerAndroidSDK
3 | Send Money
4 | Checkout
5 | Direct Charge
6 | 73f03de5-1043-4ad1-bc2e-aa4d94ebee4f
7 |
--------------------------------------------------------------------------------
/demo-app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/demo-app/src/test/java/co/thepeer/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 | android.disableAutomaticComponentCreation=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jan 29 21:57:51 WAT 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/java-demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/java-demo/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | compileSdkVersion 33
7 |
8 | defaultConfig {
9 | applicationId "com.example.java_demo"
10 | minSdkVersion 23
11 | targetSdkVersion 33
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | }
29 |
30 | dependencies {
31 | implementation project(":thepeer-android")
32 | implementation 'androidx.appcompat:appcompat:1.4.1'
33 | implementation 'com.google.android.material:material:1.5.0'
34 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
35 | implementation 'androidx.core:core-ktx:1.7.0'
36 | testImplementation 'junit:junit:4.+'
37 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
39 | }
--------------------------------------------------------------------------------
/java-demo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/java-demo/src/androidTest/java/com/example/java_demo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.java_demo;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.example.java_demo", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/java-demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/java-demo/src/main/java/com/example/java_demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.java_demo;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.widget.Button;
6 | import android.widget.TextView;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.appcompat.app.AppCompatActivity;
10 |
11 | import java.math.BigDecimal;
12 | import java.util.HashMap;
13 |
14 | import co.thepeer.sdk.Thepeer;
15 | import co.thepeer.sdk.model.ThepeerConfig;
16 | import co.thepeer.sdk.ui.ThepeerResultListener;
17 | import co.thepeer.sdk.utils.ThepeerCurrency;
18 |
19 | public class MainActivity extends AppCompatActivity {
20 |
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 |
27 | HashMap meta = new HashMap<>();
28 | // initialize Thepeer SDK
29 | Thepeer thepeer = new Thepeer.Initiate(
30 | getResources().getString(R.string.user_reference),
31 | this,
32 | new ThepeerResultListener() {
33 | @Override
34 | public void onSuccess(@NonNull String transaction) {
35 | ((TextView) findViewById(R.id.resultText)).setText(transaction.toString());
36 | }
37 |
38 | @Override
39 | public void onCancelled() {
40 |
41 | }
42 |
43 | @Override
44 | public void onError(@NonNull Throwable error) {
45 |
46 | }
47 |
48 | })
49 | .build();
50 |
51 | ((Button) findViewById(R.id.btnSendMoney)).setOnClickListener(new View.OnClickListener() {
52 | @Override
53 | public void onClick(View v) {
54 | thepeer.send(new ThepeerConfig(new BigDecimal("100000"), ThepeerCurrency.NGN, meta));
55 | }
56 | });
57 |
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
26 |
27 |
35 |
36 |
45 |
46 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/java-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/java-demo/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | java-demo
3 | Send Money
4 | Checkout
5 | Direct Charge
6 | 73f03de5-1043-4ad1-bc2e-aa4d94ebee4f
7 |
--------------------------------------------------------------------------------
/java-demo/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/java-demo/src/test/java/com/example/java_demo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.java_demo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/scripts/publish-module.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 | apply plugin: 'signing'
3 |
4 | task androidSourcesJar(type: Jar) {
5 | archiveClassifier.set('sources')
6 | if (project.plugins.findPlugin("com.android.library")) {
7 | // For Android libraries
8 | from android.sourceSets.main.java.srcDirs
9 | from android.sourceSets.main.kotlin.srcDirs
10 | } else {
11 | // For pure Kotlin libraries, in case you have them
12 | from sourceSets.main.java.srcDirs
13 | from sourceSets.main.kotlin.srcDirs
14 | }
15 | }
16 |
17 | artifacts {
18 | archives androidSourcesJar
19 | }
20 |
21 | group = PUBLISH_GROUP_ID
22 | version = PUBLISH_VERSION
23 |
24 | afterEvaluate {
25 | publishing {
26 | publications {
27 | release(MavenPublication) {
28 | // The coordinates of the library, being set from variables that
29 | // we'll set up later
30 | groupId PUBLISH_GROUP_ID
31 | artifactId PUBLISH_ARTIFACT_ID
32 | version PUBLISH_VERSION
33 |
34 | // Two artifacts, the `aar` (or `jar`) and the sources
35 | if (project.plugins.findPlugin("com.android.library")) {
36 | from components.release
37 | } else {
38 | from components.java
39 | }
40 |
41 | artifact androidSourcesJar
42 |
43 | // Mostly self-explanatory metadata
44 | pom {
45 | name = PUBLISH_ARTIFACT_ID
46 | description = 'Thepeer official Android SDK'
47 | url = 'https://github.com/thepeerstack/thepeer-android'
48 | licenses {
49 | license {
50 | name = 'Thepeer License'
51 | url = ''
52 | }
53 | }
54 | developers {
55 | developer {
56 | id = 'zsmb13'
57 | name = 'Kamsi Oleka'
58 | email = 'kamsy@thepeer.co'
59 | }
60 | developer {
61 | id = 'zsmb14'
62 | name = 'Nsikak Thompson'
63 | email = 'nsikakthompson73@gmail.com'
64 | }
65 | // Add all other devs here...
66 | }
67 |
68 | // Version control info - if you're using GitHub, follow the
69 | // format as seen here
70 | scm {
71 | connection = 'scm:git:github.com/thepeerstack/thepeer-android.git'
72 | developerConnection = 'scm:git:ssh://github.com/thepeerstack/thepeer-android.git'
73 | url = 'https://github.com/thepeerstack/thepeer-android/tree/master'
74 | }
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
81 | signing {
82 | useInMemoryPgpKeys(
83 | rootProject.ext["signingKeyId"],
84 | rootProject.ext["signingKey"],
85 | rootProject.ext["signingPassword"],
86 | )
87 | sign publishing.publications
88 | }
--------------------------------------------------------------------------------
/scripts/publish-root.gradle:
--------------------------------------------------------------------------------
1 | // Create variables with empty default values
2 | ext["ossrhUsername"] = ''
3 | ext["ossrhPassword"] = ''
4 | ext["sonatypeStagingProfileId"] = ''
5 | ext["signingKeyId"] = ''
6 | ext["signingPassword"] = ''
7 | ext["signingKey"] = ''
8 | ext["publishVersion"] = ''
9 |
10 | File secretPropsFile = project.rootProject.file('local.properties')
11 | if (secretPropsFile.exists()) {
12 | // Read local.properties file first if it exists
13 | Properties p = new Properties()
14 | new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
15 | p.each { name, value -> ext[name] = value }
16 | } else {
17 | // Use system environment variables
18 | ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
19 | ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
20 | ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
21 | ext["signingKeyId"] = System.getenv('SIGNING_KEY_ID')
22 | ext["signingPassword"] = System.getenv('SIGNING_PASSWORD')
23 | ext["signingKey"] = System.getenv('SIGNING_KEY')
24 | ext["publishVersion"] = System.getenv('PUBLISH_VERSION')
25 | }
26 |
27 |
28 | // Set up Sonatype repository
29 | nexusPublishing {
30 | repositories {
31 | sonatype {
32 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
33 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
34 | stagingProfileId = sonatypeStagingProfileId
35 | username = ossrhUsername
36 | password = ossrhPassword
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | import org.gradle.api.initialization.resolve.RepositoriesMode
2 |
3 | pluginManagement {
4 | repositories {
5 | google()
6 | mavenCentral()
7 | gradlePluginPortal()
8 | }
9 | }
10 | dependencyResolutionManagement {
11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | }
17 | rootProject.name = "thepeer-android"
18 | include ':demo-app'
19 | include ':thepeer-android'
20 | include ':java-demo'
21 |
--------------------------------------------------------------------------------
/thepeer-android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/thepeer-android/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id 'kotlin-parcelize'
5 | id 'kotlin-kapt'
6 | id 'maven-publish'
7 | }
8 |
9 |
10 | version = "1.0.0"
11 |
12 | android {
13 | compileSdkVersion 33
14 |
15 | defaultConfig {
16 | minSdkVersion 23
17 | targetSdkVersion 33
18 | versionCode 1
19 | versionName "1.0"
20 |
21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
22 | consumerProguardFiles "consumer-rules.pro"
23 | }
24 |
25 |
26 | buildFeatures {
27 | viewBinding true
28 | }
29 |
30 | buildTypes {
31 |
32 |
33 | release {
34 | minifyEnabled false
35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
36 | }
37 | }
38 | compileOptions {
39 | sourceCompatibility JavaVersion.VERSION_1_8
40 | targetCompatibility JavaVersion.VERSION_1_8
41 | }
42 | kotlinOptions {
43 | jvmTarget = '1.8'
44 | }
45 | }
46 |
47 | dependencies {
48 |
49 | implementation 'androidx.core:core-ktx:1.7.0'
50 | implementation 'androidx.appcompat:appcompat:1.4.2'
51 | implementation 'com.google.android.material:material:1.6.1'
52 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
53 |
54 | // Kotlin
55 | implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
56 | implementation 'androidx.webkit:webkit:1.4.0'
57 |
58 | // MockK kotlin mocking framework
59 | testImplementation "io.mockk:mockk:$mockkVersion"
60 |
61 | implementation 'com.google.code.gson:gson:2.8.9'
62 |
63 | // JUnit
64 | testImplementation "junit:junit:$junitVersion"
65 |
66 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
68 | androidTestImplementation 'androidx.test:rules:1.4.0'
69 | androidTestImplementation 'androidx.test:runner:1.2.0'
70 |
71 | // Robolectric
72 | testImplementation "org.robolectric:robolectric:$robolectricVersion"
73 | testImplementation "org.robolectric:shadows-multidex:$robolectricVersion"
74 | }
75 |
76 | ext {
77 | PUBLISH_GROUP_ID = 'co.thepeer'
78 | PUBLISH_VERSION = publishVersion
79 | PUBLISH_ARTIFACT_ID = 'thepeer-android'
80 | }
81 |
82 | apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
83 | apply from: "$rootDir/thepeer-android/config/ktlint.gradle"
--------------------------------------------------------------------------------
/thepeer-android/config/ktlint.gradle:
--------------------------------------------------------------------------------
1 | configurations {
2 | ktlint
3 | }
4 |
5 | dependencies {
6 | ktlint("com.pinterest:ktlint:$ktlintVersion") {
7 | attributes {
8 | attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL))
9 | }
10 | }
11 | }
12 |
13 | tasks.register("ktlint", JavaExec) {
14 | group = "verification"
15 | description = "Check Kotlin code style."
16 | classpath = configurations.ktlint
17 | main = "com.pinterest.ktlint.Main"
18 | args "**/*.kt" , "!**/build/**/*.kt"
19 | }
20 |
21 | tasks.register("ktlintFormat", JavaExec) {
22 | group = "formatting"
23 | description = "Fix Kotlin code style deviations."
24 | classpath = configurations.ktlint
25 | main = "com.pinterest.ktlint.Main"
26 | args "-F", "**/*.kt" , "!**/build/**/*.kt"
27 | }
28 |
29 | tasks.register("ktlintAndroidStudio", JavaExec) {
30 | description = "Setup Android Studio to use the same code style as ktlint."
31 | classpath = configurations.ktlint
32 | main = "com.pinterest.ktlint.Main"
33 | args "--android", "applyToIDEAProject", "-y"
34 | }
--------------------------------------------------------------------------------
/thepeer-android/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thepeerstack/thepeer-android/353656594ae387a198c9836b15caf2ab58a0bd91/thepeer-android/consumer-rules.pro
--------------------------------------------------------------------------------
/thepeer-android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/thepeer-android/src/androidTest/java/co/thepeer/sdk/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import junit.framework.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("co.thepeer.sdk.test", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/Thepeer.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk
2 |
3 | import android.content.Context
4 | import android.content.pm.PackageManager
5 | import androidx.activity.result.ActivityResultLauncher
6 | import androidx.appcompat.app.AppCompatActivity
7 | import co.thepeer.sdk.model.ThepeerConfig
8 | import co.thepeer.sdk.model.ThepeerParam
9 | import co.thepeer.sdk.model.ThepeerResult
10 | import co.thepeer.sdk.model.ThepeerSDKType
11 | import co.thepeer.sdk.ui.ThepeerResultContract
12 | import co.thepeer.sdk.ui.ThepeerResultListener
13 | import co.thepeer.sdk.utils.ThepeerConstants
14 |
15 | /**
16 | * This is Thepeer SDK class instance.
17 | * @param publicKey -> Required to authenticate the merchant
18 | * @param userReference -> Customer indexed user reference from ThePeer
19 | * @param resultRegistry -> used to register the activity for result
20 | * @param activity -> Required to launch the drop in UI activity
21 | */
22 | class Thepeer internal constructor(
23 | private val publicKey: String,
24 | private val userReference: String,
25 | private var resultRegistry: ActivityResultLauncher,
26 | private val activity: AppCompatActivity
27 | ) {
28 |
29 | /**
30 | * Config builder for initialisation parameters
31 | * @param userReference -> Customer indexed user reference from ThePeer
32 | * @param activity -> Required to launch the drop in UI activity
33 | */
34 | class Initiate(
35 | private val userReference: String,
36 | private val activity: AppCompatActivity,
37 | private val resultListener: ThepeerResultListener
38 |
39 | ) {
40 | private var publicKey = ""
41 |
42 | val resultRegistry = activity.registerForActivityResult(
43 | ThepeerResultContract(),
44 | activity.activityResultRegistry
45 | ) { chargeResult ->
46 | when (chargeResult) {
47 | is ThepeerResult.Success -> resultListener.onSuccess(chargeResult.response)
48 | is ThepeerResult.Error -> resultListener.onError(Throwable(chargeResult.error))
49 | ThepeerResult.Cancelled -> resultListener.onCancelled()
50 | }
51 | }
52 |
53 | init {
54 | publicKey = getPublicKeyFromManifest(activity)
55 |
56 | if (publicKey.isEmpty()) {
57 | error(
58 | "Add publicKey from business " +
59 | "dashboard as meta data to manifest file. Check documentation"
60 | )
61 | }
62 | }
63 |
64 | private fun getPublicKeyFromManifest(context: Context): String {
65 | val applicationInfo = context.packageManager.getApplicationInfo(
66 | context.packageName,
67 | PackageManager.GET_META_DATA
68 | )
69 | return applicationInfo.metaData?.getString(ThepeerConstants.PUBLIC_KEY_FROM_MANIFEST)
70 | .orEmpty()
71 | }
72 |
73 | fun build(): Thepeer {
74 | return Thepeer(
75 | publicKey,
76 | userReference,
77 | resultRegistry,
78 | activity
79 | )
80 | }
81 | }
82 |
83 | /**
84 | * This function will be called to launch ThePeer Send Money Widget.
85 | * @param config is the transaction specific configurations.
86 | */
87 | fun send(
88 | config: ThepeerConfig
89 | ) {
90 | val params = ThepeerParam(
91 | publicKey,
92 | getSdkType(ThepeerSDKType.SEND),
93 | config.amount,
94 | config.currency,
95 | userReference,
96 | null,
97 | config.meta
98 | )
99 | resultRegistry.launch(params)
100 | }
101 |
102 | /**
103 | * This function will be called to launch ThePeer Checkout Widget
104 | * @param emailAddress is the email address of the user.
105 | * @param config is the transaction specific configurations.
106 | */
107 | fun checkout(
108 | emailAddress: String,
109 | config: ThepeerConfig
110 | ) {
111 | val params = ThepeerParam(
112 | publicKey,
113 | getSdkType(ThepeerSDKType.CHECKOUT),
114 | config.amount,
115 | config.currency,
116 | userReference,
117 | emailAddress,
118 | config.meta
119 | )
120 |
121 | resultRegistry.launch(params)
122 | }
123 |
124 | /**
125 | * This function will be called to launch ThePeer Direct Charge Widget
126 | * @param config is the transaction specific configurations.
127 | */
128 | fun directCharge(
129 | config: ThepeerConfig
130 | ) {
131 | val params = ThepeerParam(
132 | publicKey,
133 | getSdkType(ThepeerSDKType.DIRECT_CHARGE),
134 | config.amount,
135 | config.currency,
136 | userReference,
137 | null,
138 | config.meta
139 | )
140 | resultRegistry.launch(params)
141 | }
142 |
143 | private fun getSdkType(type: Enum): String {
144 | return when (type) {
145 | ThepeerSDKType.SEND -> {
146 | return ThepeerConstants.SEND
147 | }
148 |
149 | ThepeerSDKType.CHECKOUT -> {
150 | return ThepeerConstants.CHECKOUT
151 | }
152 |
153 | ThepeerSDKType.DIRECT_CHARGE -> {
154 | return ThepeerConstants.DIRECT_CHARGE
155 | }
156 |
157 | else -> ""
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/model/ThepeerEvent.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class ThepeerEvent(
8 | val event: String
9 | ) : Parcelable
10 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/model/ThepeerParam.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 | import java.math.BigDecimal
6 |
7 | /**
8 | * These are resolved params from the user provided config to initiate a transaction
9 | */
10 | @Parcelize
11 | data class ThepeerParam(
12 | val publicKey: String,
13 | val sdkType: String,
14 | val amount: BigDecimal,
15 | val currency: String,
16 | val userReference: String,
17 | val emailAddress: String? = null,
18 | val meta: Map
19 | ) : Parcelable
20 |
21 | enum class ThepeerSDKType { SEND, CHECKOUT, DIRECT_CHARGE, NONE }
22 |
23 | /**
24 | * This is the public data class for getting user config for a transaction.
25 | */
26 | data class ThepeerConfig(
27 | val amount: BigDecimal,
28 | val currency: String,
29 | val meta: Map = emptyMap()
30 | )
31 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/model/ThepeerResult.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | sealed class ThepeerResult : Parcelable {
7 | @Parcelize
8 | data class Success(val response: String) : ThepeerResult()
9 |
10 | @Parcelize
11 | data class Error(val error: String) : ThepeerResult()
12 |
13 | @Parcelize
14 | object Cancelled : ThepeerResult()
15 | }
16 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/ui/ThepeerResultContract.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.ui
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import androidx.activity.result.contract.ActivityResultContract
7 | import co.thepeer.sdk.model.ThepeerParam
8 | import co.thepeer.sdk.model.ThepeerResult
9 | import co.thepeer.sdk.ui.activity.ThepeerSdkActivity
10 | import co.thepeer.sdk.utils.ThepeerConstants
11 |
12 | internal class ThepeerResultContract : ActivityResultContract() {
13 | override fun createIntent(context: Context, input: ThepeerParam): Intent {
14 | return Intent(context, ThepeerSdkActivity::class.java).apply {
15 | putExtra(ThepeerConstants.THE_PEER_PARAMS, input)
16 | }
17 | }
18 |
19 | override fun parseResult(resultCode: Int, intent: Intent?): ThepeerResult {
20 | if (resultCode == Activity.RESULT_CANCELED) {
21 | return ThepeerResult.Cancelled
22 | }
23 |
24 | if (resultCode == Activity.RESULT_OK && intent != null) {
25 | return intent.getParcelableExtra(ThepeerConstants.TRANSACTION_RESULT)
26 | ?: error(NO_RESULT_ERROR)
27 | }
28 |
29 | error(NO_RESULT_ERROR)
30 | }
31 |
32 | companion object {
33 | const val NO_RESULT_ERROR =
34 | "Transaction was not confirmed for some reason. Please contact ThePeer support"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/ui/ThepeerResultListener.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.ui
2 |
3 | interface ThepeerResultListener {
4 | fun onSuccess(response: String)
5 |
6 | fun onError(error: Throwable)
7 |
8 | fun onCancelled()
9 | }
10 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/ui/activity/ThepeerSdkActivity.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.ui.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Intent
5 | import android.os.Build
6 | import android.os.Bundle
7 | import android.webkit.WebResourceError
8 | import android.webkit.WebResourceRequest
9 | import android.webkit.WebView
10 | import android.webkit.WebViewClient
11 | import android.widget.Toast
12 | import androidx.activity.OnBackPressedCallback
13 | import androidx.appcompat.app.AppCompatActivity
14 | import androidx.core.view.isVisible
15 | import co.thepeer.sdk.R
16 | import co.thepeer.sdk.databinding.SdkActivityBinding
17 | import co.thepeer.sdk.model.ThepeerParam
18 | import co.thepeer.sdk.model.ThepeerResult
19 | import co.thepeer.sdk.utils.Logger
20 | import co.thepeer.sdk.utils.ThepeerConstants
21 | import co.thepeer.sdk.utils.Urls
22 | import co.thepeer.sdk.utils.WebInterface
23 | import co.thepeer.sdk.utils.isNetworkConnectionAvailable
24 |
25 | internal class ThepeerSdkActivity : AppCompatActivity() {
26 | private var params: ThepeerParam? = null
27 | private var url: String = ""
28 | private lateinit var binding: SdkActivityBinding
29 | private var isLoaded = false
30 | override fun onCreate(savedInstanceState: Bundle?) {
31 | super.onCreate(savedInstanceState)
32 | binding = SdkActivityBinding.inflate(layoutInflater)
33 | setContentView(binding.root)
34 | onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
35 |
36 | params = if (Build.VERSION.SDK_INT >= 33) {
37 | intent.getParcelableExtra(
38 | ThepeerConstants.THE_PEER_PARAMS,
39 | ThepeerParam::class.java
40 | )
41 | } else {
42 | intent.getParcelableExtra(ThepeerConstants.THE_PEER_PARAMS)
43 | }
44 |
45 | if (params == null) {
46 | throw IllegalStateException("Params should not be null. Initialize thePeer using this function Thepeer.Builder(...)")
47 | }
48 |
49 | params?.let {
50 | url = Urls.createTransactionUrl(it)
51 | showWebView()
52 | Logger.log(this, url)
53 | }
54 |
55 | binding.includeRetryView.closeBtn.setOnClickListener {
56 | finish()
57 | }
58 | }
59 |
60 | @SuppressLint("SetJavaScriptEnabled")
61 | private fun setupWebView(transactionUrl: String) {
62 | binding.webViewPeer.apply {
63 | settings.apply {
64 | javaScriptEnabled = true
65 | javaScriptCanOpenWindowsAutomatically = true
66 | domStorageEnabled = true
67 | }
68 | }
69 | binding.webViewPeer.webViewClient = object : WebViewClient() {
70 | override fun onPageFinished(view: WebView, url: String) {
71 | super.onPageFinished(view, url)
72 | isLoaded = true
73 | binding.webViewPeer.isVisible = true
74 | }
75 |
76 | override fun onReceivedError(
77 | view: WebView?,
78 | request: WebResourceRequest?,
79 | error: WebResourceError?
80 | ) {
81 | if (error != null) {
82 | showNetworkErrorRetryView()
83 | }
84 | }
85 |
86 | override fun shouldOverrideUrlLoading(
87 | view: WebView?,
88 | request: WebResourceRequest?
89 | ): Boolean {
90 | startActivity(Intent(Intent.ACTION_VIEW, request?.url))
91 | return true
92 | }
93 | }
94 |
95 | binding.webViewPeer.addJavascriptInterface(
96 | WebInterface { results ->
97 | redirectWithResult(results)
98 | },
99 | "Android"
100 | )
101 |
102 | binding.webViewPeer.loadUrl(transactionUrl)
103 | }
104 |
105 | override fun onDestroy() {
106 | super.onDestroy()
107 | clearWebViewValues()
108 | redirectWithResult(ThepeerResult.Cancelled)
109 | }
110 |
111 | private fun clearWebViewValues() {
112 | params = null
113 | binding.webViewPeer.loadUrl("about:blank")
114 | binding.webViewPeer.clearHistory()
115 | }
116 |
117 | private fun showNetworkErrorRetryView() {
118 | if (!isNetworkConnectionAvailable) {
119 | if (!isLoaded) {
120 | hideWebView()
121 | binding.includeRetryView.retryView.isVisible = true
122 | } else {
123 | Toast.makeText(this, getString(R.string.retry_text), Toast.LENGTH_LONG)
124 | .show()
125 | }
126 | } else {
127 | Toast.makeText(this, "Something went wrong. Contact thePeer support", Toast.LENGTH_LONG)
128 | .show()
129 | }
130 |
131 | binding.includeRetryView.retryButton.setOnClickListener {
132 | if (isNetworkConnectionAvailable && url.isNotBlank()) {
133 | hideRetryView()
134 | setupWebView(url)
135 | }
136 | }
137 | }
138 |
139 | private fun hideRetryView() {
140 | binding.includeRetryView.retryView.isVisible = false
141 | binding.loading.isVisible = true
142 | }
143 |
144 | private fun showWebView() {
145 | binding.loading.isVisible = true
146 | if (isNetworkConnectionAvailable) {
147 | setupWebView(url)
148 | } else {
149 | showNetworkErrorRetryView()
150 | }
151 | }
152 |
153 | private fun hideWebView() {
154 | binding.webViewPeer.isVisible = false
155 | binding.loading.isVisible = false
156 | }
157 |
158 | private fun redirectWithResult(result: ThepeerResult) {
159 | val resultData = Intent()
160 | resultData.putExtra(ThepeerConstants.TRANSACTION_RESULT, result)
161 | setResult(AppCompatActivity.RESULT_OK, resultData)
162 | finish()
163 | }
164 |
165 | private val onBackPressedCallback: OnBackPressedCallback =
166 | object : OnBackPressedCallback(true) {
167 | override fun handleOnBackPressed() {
168 | params = null
169 | if (!isNetworkConnectionAvailable) {
170 | finish()
171 | }
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/Environment.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | internal enum class Environment(val url: String) {
4 | PROD(""),
5 | TEST("")
6 | }
7 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/Extensions.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | import android.content.Context
4 | import android.net.ConnectivityManager
5 | import android.net.NetworkCapabilities
6 |
7 | fun String.getLastPart() = this.split(".")[1]
8 | fun String.getFirstPart() = this.split(".")[0]
9 |
10 | /**
11 | * [returns] true if the device is connected to the internet. False otherwise.
12 | */
13 | val Context.isNetworkConnectionAvailable: Boolean
14 | get() {
15 | val connectivityManager =
16 | getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
17 | val nw = connectivityManager.activeNetwork ?: return false
18 | val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
19 | return when {
20 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
21 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
22 | else -> false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/Logger.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | import android.util.Log
4 | import androidx.viewbinding.BuildConfig
5 |
6 | internal object Logger {
7 |
8 | private val isDebugEnabled = BuildConfig.DEBUG
9 |
10 | fun log(o: Any, message: String, level: LEVEL = LEVEL.DEBUG) {
11 | if (isDebugEnabled) {
12 | val simpleName = o.javaClass.simpleName + ":ThePeer"
13 | when (level) {
14 | LEVEL.DEBUG -> Log.d(simpleName, message)
15 | LEVEL.INFO -> Log.i(simpleName, message)
16 | LEVEL.ERROR -> Log.e(simpleName, message)
17 | LEVEL.VERBOSE -> Log.v(simpleName, message)
18 | LEVEL.WARN -> Log.w(simpleName, message)
19 | }
20 | }
21 | }
22 |
23 | enum class LEVEL {
24 | DEBUG, INFO, ERROR, VERBOSE, WARN
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/ThepeerConstants.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | internal class ThepeerConstants {
4 | companion object {
5 | const val THE_PEER_PARAMS = "peer_params"
6 | const val SEND = "send"
7 | const val CHECKOUT = "checkout"
8 | const val DIRECT_CHARGE = "directCharge"
9 | const val TRANSACTION_RESULT = "transaction_result"
10 | const val PUBLIC_KEY_FROM_MANIFEST = "co.thepeer.PublicKey"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/ThepeerCurrency.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | class ThepeerCurrency {
4 | companion object {
5 | const val NGN = "NGN"
6 | const val USD = "USD"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/Urls.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | import co.thepeer.sdk.model.ThepeerParam
4 | import com.google.gson.Gson
5 |
6 | internal object Urls {
7 |
8 | fun createTransactionUrl(data: ThepeerParam): String {
9 | var BASE_URL = "https://chain.thepeer.co?"
10 |
11 | val params = mapOf(
12 | "publicKey" to data.publicKey,
13 | "amount" to data.amount,
14 | "currency" to data.currency,
15 | "userReference" to data.userReference,
16 | "email" to data.emailAddress,
17 | "sdkType" to data.sdkType,
18 | "meta" to Gson().toJson(data.meta)
19 | )
20 |
21 | for (k in params.keys) {
22 | if (params[k] != null) {
23 | val value = params[k]
24 | BASE_URL = "$BASE_URL$k=$value&"
25 | }
26 | }
27 | return BASE_URL.dropLast(1)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/java/co/thepeer/sdk/utils/WebInterface.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk.utils
2 |
3 | import android.util.Log
4 | import android.webkit.JavascriptInterface
5 | import co.thepeer.sdk.model.ThepeerEvent
6 | import co.thepeer.sdk.model.ThepeerResult
7 | import com.google.gson.Gson
8 |
9 | /**
10 | * This webinterface handles events from the webview
11 | */
12 | internal class WebInterface(private val redirect: (ThepeerResult) -> Unit) {
13 |
14 | private val SEND_INSUFFICIENT_FUNDS = "send.insufficient_funds"
15 | private val SEND_USER_INSUFFICIENT_FUNDS = "send.user_insufficient_funds"
16 | private val SEND_SUCCESS = "send.success"
17 | private val DIRECT_CHARGE_INSUFFICIENT_FUNDS = "direct_debit.insufficient_funds"
18 | private val DIRECT_CHARGE_USER_INSUFFICIENT_FUNDS =
19 | "direct_debit.user_insufficient_funds"
20 | private val DIRECT_CHARGE_SUCCESS = "direct_debit.success"
21 | private val DIRECT_CHARGE_BUSINESS_DECLINE = "direct_debit.business_decline"
22 | private val DIRECT_CHARGE_USER_DECLINE = "direct_debit.user_decline"
23 | private val CHECKOUT_INSUFFICIENT_FUNDS = "checkout.insufficient_funds"
24 | private val CHECKOUT_USER_INSUFFICIENT_FUNDS = "checkout.user_insufficient_funds"
25 | private val CHECKOUT_SUCCESS = "checkout.success"
26 | private val CHECKOUT_BUSINESS_DECLINE = "checkout.business_decline"
27 | private val CHECKOUT_USER_DECLINE = "checkout.user_decline"
28 | private val SEND_CLOSE = "send.close"
29 | private val DIRECT_CHARGE_CLOSE = "direct_debit.close"
30 | private val CHECKOUT_CLOSE = "checkout.close"
31 |
32 | inline fun convertToGsonFromString(json: String): T {
33 | val adapter = Gson().getAdapter(T::class.java)
34 | return adapter.fromJson(json)
35 | }
36 |
37 | fun handleSendEvent(event: ThepeerEvent, response: String) {
38 | when (event.event) {
39 | SEND_SUCCESS -> {
40 | redirect(ThepeerResult.Success(response))
41 | }
42 |
43 | SEND_CLOSE -> {
44 | redirect(ThepeerResult.Cancelled)
45 | }
46 |
47 | else -> {
48 | redirect(ThepeerResult.Error(response))
49 | }
50 | }
51 | }
52 |
53 | fun handleCheckoutEvent(event: ThepeerEvent, response: String) {
54 | when (event.event) {
55 | CHECKOUT_SUCCESS -> {
56 | redirect(ThepeerResult.Success(response))
57 | }
58 |
59 | CHECKOUT_CLOSE -> {
60 | redirect(ThepeerResult.Cancelled)
61 | }
62 |
63 | else -> {
64 | redirect(ThepeerResult.Error(response))
65 | }
66 | }
67 | }
68 |
69 | fun handleDirectDebitEvent(event: ThepeerEvent, response: String) {
70 | when (event.event) {
71 | DIRECT_CHARGE_SUCCESS -> {
72 | redirect(ThepeerResult.Success(response))
73 | }
74 |
75 | DIRECT_CHARGE_CLOSE -> {
76 | redirect(ThepeerResult.Cancelled)
77 | }
78 |
79 | else -> {
80 | redirect(ThepeerResult.Error(response))
81 | }
82 | }
83 | }
84 |
85 | @JavascriptInterface
86 | fun sendResponse(response: String) {
87 | Log.e(" na this response be this", response)
88 | try {
89 | val event: ThepeerEvent = convertToGsonFromString(response)
90 | when (event.event.getFirstPart()) {
91 | "send" -> {
92 | handleSendEvent(event, response)
93 | }
94 |
95 | "checkout" -> {
96 | handleCheckoutEvent(event, response)
97 | }
98 |
99 | "direct_debit" -> {
100 | handleDirectDebitEvent(event, response)
101 | }
102 | }
103 | } catch (e: Exception) {
104 | Log.e("WebInterface", e.message.toString())
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/drawable/close_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/drawable/dialog_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/drawable/warning.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/layout/fragment_thepeer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
23 |
24 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/layout/retry_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
28 |
29 |
40 |
41 |
57 |
58 |
59 |
60 |
69 |
70 |
79 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/layout/sdk_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
25 |
26 |
38 |
39 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #0066FF
5 | #0066FF
6 | #0066FF
7 | #24366B
8 |
9 | #43979797
10 | #F7FCFF
11 | #586283
12 |
13 | #7AA8EE
14 | #DB3838
15 |
16 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 56dp
4 | 20sp
5 |
6 | 23sp
7 | 16sp
8 | 14sp
9 |
10 | 15sp
11 | 13sp
12 | 30sp
13 |
14 | 21dp
15 | 24dp
16 |
17 |
18 | 16sp
19 | 14sp
20 | 24sp
21 | 32sp
22 |
23 |
24 | 45dp
25 |
26 |
27 | 40dp
28 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Something went wrong
4 | Something went wrong trying to load the Webview. Please try again
5 | Try again
6 |
--------------------------------------------------------------------------------
/thepeer-android/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
17 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/thepeer-android/src/test/java/co/thepeer/sdk/MockData.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk
2 |
3 | object MockData {
4 |
5 | const val sendSuccess =
6 | "{\"event\":\"send.success\",\"data\":{\"id\":\"ea3a2ccb-ed0b-4bb6-8a7b-89a09a77cd62\",\"remark\":\"okk\",\"amount\":10000,\"charge\":0,\"refund\":false,\"type\":\"peer\",\"currency\":\"NGN\",\"status\":\"success\",\"mode\":\"debit\",\"meta\":{\"city\":\"Uyo\"},\"reference\":\"a5092442d5e3ddjjdjd\",\"checkout\":null,\"peer\":{\"business\":{\"name\":\"Cash App\",\"logo\":\"https://image.png\",\"logo_colour\":\"#77cc33\"},\"user\":{\"name\":\"user\",\"identifier\":\"sharp\",\"identifier_type\":\"username\"}},\"user\":{\"name\":\"user\",\"identifier\":\"doreen\",\"identifier_type\":\"username\",\"email\":\"user@gmail.com\",\"reference\":\"73f03de5-1043-4adhhfh\",\"created_at\":\"2021-04-19T19:50:26.000000Z\",\"updated_at\":\"2022-02-14T22:58:25.000000Z\"},\"channel\":\"send\",\"created_at\":\"2022-05-18T20:34:28.000000Z\",\"updated_at\":\"2022-05-18T20:34:28.000000Z\"}}"
7 |
8 | const val sendClose = "{\"event\":\"send.close\"}"
9 | const val sendError = "{\"event\":\"send.user_insufficient_funds\"}"
10 |
11 | const val checkoutSuccess =
12 | "{\"event\":\"checkout.success\",\"data\":{\"id\":\"ea3a2ccb-ed0b-4bb6-8a7b-89a09a77cd62\",\"remark\":\"okk\",\"amount\":10000,\"charge\":0,\"refund\":false,\"type\":\"peer\",\"currency\":\"NGN\",\"status\":\"success\",\"mode\":\"debit\",\"meta\":{\"city\":\"Uyo\"},\"reference\":\"a5092442d5e3ddjjdjd\",\"checkout\":null,\"peer\":{\"business\":{\"name\":\"Cash App\",\"logo\":\"https://image.png\",\"logo_colour\":\"#77cc33\"},\"user\":{\"name\":\"user\",\"identifier\":\"sharp\",\"identifier_type\":\"username\"}},\"user\":{\"name\":\"user\",\"identifier\":\"doreen\",\"identifier_type\":\"username\",\"email\":\"user@gmail.com\",\"reference\":\"73f03de5-1043-4adhhfh\",\"created_at\":\"2021-04-19T19:50:26.000000Z\",\"updated_at\":\"2022-02-14T22:58:25.000000Z\"},\"channel\":\"send\",\"created_at\":\"2022-05-18T20:34:28.000000Z\",\"updated_at\":\"2022-05-18T20:34:28.000000Z\"}}"
13 |
14 | const val checkoutClose = "{\"event\":\"checkout.close\"}"
15 |
16 | const val directDebitSuccess =
17 | "{\"event\":\"direct_debit.success\",\"data\":{\"id\":\"ea3a2ccb-ed0b-4bb6-8a7b-89a09a77cd62\",\"remark\":\"okk\",\"amount\":10000,\"charge\":0,\"refund\":false,\"type\":\"peer\",\"currency\":\"NGN\",\"status\":\"success\",\"mode\":\"debit\",\"meta\":{\"city\":\"Uyo\"},\"reference\":\"a5092442d5e3ddjjdjd\",\"checkout\":null,\"peer\":{\"business\":{\"name\":\"Cash App\",\"logo\":\"https://image.png\",\"logo_colour\":\"#77cc33\"},\"user\":{\"name\":\"user\",\"identifier\":\"sharp\",\"identifier_type\":\"username\"}},\"user\":{\"name\":\"user\",\"identifier\":\"doreen\",\"identifier_type\":\"username\",\"email\":\"user@gmail.com\",\"reference\":\"73f03de5-1043-4adhhfh\",\"created_at\":\"2021-04-19T19:50:26.000000Z\",\"updated_at\":\"2022-02-14T22:58:25.000000Z\"},\"channel\":\"send\",\"created_at\":\"2022-05-18T20:34:28.000000Z\",\"updated_at\":\"2022-05-18T20:34:28.000000Z\"}}"
18 |
19 | const val directDebitClose = "{\"event\":\"direct_debit.close\"}"
20 | }
21 |
--------------------------------------------------------------------------------
/thepeer-android/src/test/java/co/thepeer/sdk/WebInterfaceTest.kt:
--------------------------------------------------------------------------------
1 | package co.thepeer.sdk
2 |
3 | import android.util.Log
4 | import co.thepeer.sdk.model.ThepeerEvent
5 | import co.thepeer.sdk.utils.WebInterface
6 | import io.mockk.every
7 | import io.mockk.mockk
8 | import io.mockk.mockkStatic
9 | import io.mockk.verify
10 | import org.junit.Assert.assertEquals
11 | import org.junit.Before
12 | import org.junit.Test
13 |
14 | class WebInterfaceTest {
15 |
16 | private lateinit var webInterface: WebInterface
17 |
18 | @Before
19 | fun setup() {
20 | webInterface = WebInterface(mockk())
21 | mockkStatic(Log::class)
22 | mockk(relaxed = true)
23 | every { Log.d(any(), any()) } returns 0
24 | every { Log.e(any(), any()) } returns 0
25 | }
26 |
27 | @Test
28 | fun `handle send event - success`() {
29 | val event = webInterface.convertToGsonFromString(MockData.sendSuccess) as ThepeerEvent
30 | webInterface.sendResponse(MockData.sendSuccess)
31 | verify {
32 | webInterface.handleSendEvent(event, MockData.sendSuccess)
33 | }
34 |
35 | assertEquals("send.success", event.event)
36 | }
37 |
38 | @Test
39 | fun `handle send event - close`() {
40 | val event = webInterface.convertToGsonFromString(MockData.sendClose) as ThepeerEvent
41 | webInterface.sendResponse(MockData.sendClose)
42 | verify {
43 | webInterface.handleSendEvent(event, MockData.sendClose)
44 | }
45 |
46 | assertEquals("send.close", event.event)
47 | }
48 |
49 | @Test
50 | fun `handle checkout event - success`() {
51 | val event = webInterface.convertToGsonFromString(MockData.checkoutSuccess) as ThepeerEvent
52 | webInterface.sendResponse(MockData.checkoutSuccess)
53 | verify {
54 | webInterface.handleCheckoutEvent(event, MockData.checkoutSuccess)
55 | }
56 |
57 | assertEquals("checkout.success", event.event)
58 | }
59 |
60 | @Test
61 | fun `handle checkout event - close`() {
62 | val event = webInterface.convertToGsonFromString(MockData.checkoutClose) as ThepeerEvent
63 | webInterface.sendResponse(MockData.checkoutClose)
64 | verify {
65 | webInterface.handleCheckoutEvent(event, MockData.checkoutClose)
66 | }
67 |
68 | assertEquals("checkout.close", event.event)
69 | }
70 |
71 | @Test
72 | fun `handle direct debit event - success`() {
73 | val event =
74 | webInterface.convertToGsonFromString(MockData.directDebitSuccess) as ThepeerEvent
75 | webInterface.sendResponse(MockData.directDebitSuccess)
76 | verify {
77 | webInterface.handleDirectDebitEvent(event, MockData.directDebitSuccess)
78 | }
79 |
80 | assertEquals("direct_debit.success", event.event)
81 | }
82 |
83 | @Test
84 | fun `handle direct debit event - close`() {
85 | val event = webInterface.convertToGsonFromString(MockData.directDebitClose) as ThepeerEvent
86 | webInterface.sendResponse(MockData.directDebitClose)
87 | verify {
88 | webInterface.handleDirectDebitEvent(event, MockData.directDebitClose)
89 | }
90 |
91 | assertEquals("direct_debit.close", event.event)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------